Git入门教程
本文最后更新于 749 天前,其中的信息可能已经有所发展或是发生改变。

什么是git

Git 是一个流行的开源的分布式版本控制系统。它由 Linus Torvalds 于 2005 年创建,此后一直由 Junio Hamano 维护。
...待续

本文章暂主要说明Windows下Git安装与使用

安装配置

下载安装包

作为开源项目,你可以很轻松的找到它的二进制可执行文件分发包,下载推荐选择最新发行版,本文将选择官方站点进行下载:Download for Windows
官方将其分为三种,Standalone Installer、Portable ("thumbdrive edition")与Using winget tool,即独立安装程序、便携版与使用winget工具,如无特殊要求,国内用户推荐使用Standalone Installer,对于选择32位或64位,推荐根据操作系统选择相应的版本,如何查询请通过Bing或Google等搜索引擎检索。

安装Git

1. 运行刚刚下载的Git可执行文件,首先会显示GPLv2的协议内容,如同意,单击Next进入下一步。
2. 选择安装路径,默认安装路径位Program Files/Git,单击Next进入下一步。
3. 进入功能选择页面,以2.39.1版本为例,有以下选择项,最后括号内为说明:

  • Additional icons
    • on the Desktop (添加桌面图标)
  • Windows Explorer integration
    • Git Bash Here (添加Git Bash到鼠标右键)
    • Git GUI Here (添加Git GUI到鼠标右键)
  • Git LFs (Large File Support) (大文件支持)
  • Associate .git* configuration files with the default text editor (关联.git文件)
  • Associate .sh files to be run with Bash (关联.sh文件)
  • Check daily for Git for Windows updates (每天检查Git更新)
  • (NEW!) Add a Git Bash Profile to Windows Terminal (将Git Bash配置文件添加到Windows终端内,需配合Windows Terminal使用)
  • (NEW!) Scalar (Git add-on to manage large-scale repositories) (管理大规模存储库的Git插件)

选择需要的配置(一般默认即可),单击Next进入下一步。
4. 选择开始菜单文件夹,方框内Git可改为其他名称,也可单击Browse...选择其他文件夹,如果不需要加入开始菜单,将下方Don't create a Start Menu folder打勾,修改完毕后,单击Next进入下一步。
5. 选择Git默认编辑器,Git内置多种编辑器支持,选择一个自己熟悉的编辑器,新手且需降低学习成本者,可使用Visual Studio Code(免费)或者Sublime Text(收费,可试用),但都需到对应官网下载安装后方可进行下一步,选择完毕后,单击Next进入下一步。

6. 选择初始化新项目(存储库)的主干名称

  • 第一个为让Git自己决定,即master,如需要,将来也可以修改为其他名称。
  • 第二个为自行决定,默认为main

选择需要的名称(一般默认即可),单击Next进入下一步。
注:第二个选项New,意思为很多团队将默认主干名称修改为了main,因为master被认为是带有歧视黑人的意味。(反正Linux没改)
7. 调整path环境变量,每个选项翻译如下:

Use Git from Git Bash only 
This is the most cautious choice as your PATH will not be modified at all. You will only be able to use the Git command line tools from Git Bash.
只从Git Bash中使用Git 
这是最谨慎的选择,因为你的PATH根本就不会被修改。你只能从 Git Bash 中使用 Git 命令行工具。

Git from the command line and also from 3rd-party software
(Recommended) This option adds only some minimal Git wrappers to your PATH to avoid cluttering your environment with optional Unix tools.
You will be able to use Git from Git Bash, the Command Prompt and the Windows PowerShell as well as any third-party software looking for Git in PATH.
从命令行和第三方软件中使用Git
(推荐) 这个选项只在你的PATH中添加一些最小的Git包装器,以避免用可选的Unix工具使你的环境变得混乱。
你将能够从Git Bash、命令提示符和Windows PowerShell以及任何在PATH中寻找Git的第三方软件中使用Git。

Use Git and optional Unix tools from the Command Prompt 
Both Git and the optional Unix tools will be added to your PATH.
Warning: This will override Windows tools like "find" and "sort". Only use this option if you understand the implications.
从命令提示符中使用Git和可选的Unix工具 
Git和可选的Unix工具都会被添加到你的 PATH 中。
警告:这将覆盖像 "查找" 和 "排序" 这样的Windows工具。只有在理解其含义的情况下才可以使用这个选项。

如不熟悉,请默认第二个选项,然后单击Next进入下一步。
8. 选择SSH执行程序

  • 第一个为让Git自带的ssh程序。
  • 第二个为用户安装的openssh或相关的程序,Git将在环境变量PATH中寻找。

如不熟悉,请默认第一个选项,然后单击Next进入下一步。
9. 选择HTTPS后端传输,每个选项翻译如下:

Use the OpenSSL library 
Server certificates will be validated using the ca-bundle.crt file.
使用OpenSSL库 
服务器证书将使用ca-bundle.crt文件进行验证。
	
Use the native Windows Secure Channel library 
Server certificates will be validated using Windows Certificate Stores.
This option also allows you to use your company's internal Root CA certificates distributed e.g. via Active Directory Domain Services.
使用本地Windows安全通道库 
服务器证书将使用Windows证书库进行验证。
这个选项也允许你使用你公司内部的根CA证书,例如通过活动目录域服务分发。

如不熟悉,请默认第一个选项,然后单击Next进入下一步。
10. 配置行尾转换符,每个选项翻译如下:

Checkout Windows-style, commit Unix-style line endings 
Git will convert LF to CRLF when checking out text files. When committing text files, CRLF will be converted to LF. For cross-platform projects, this is the recommended setting on Windows ("core.autocrif" is set to "true").
签出Windows风格,提交Unix风格的行尾 
当签出文本文件时,Git 会将 LF 转换为 CRLF。当提交文本文件时,CRLF将被转换为LF。对于跨平台的项目,这是Windows下的推荐设置("core.autocrif "设置为 "true")。

Checkout as-is, commit Unix-style line endings 
Git will not perform any conversion when checking out text files. When committing text files, CRLF will be converted to LF. For cross-platform projects, this is the recommended setting on Unix ("core.autocrif" is set to "input").
按原样签出,提交Unix风格的行尾 
在签出文本文件时,Git 不会进行任何转换。当提交文本文件时,CRLF将被转换为LF。对于跨平台项目,这是Unix平台上的推荐设置("core.autocrif "设置为 "input")。

Checkout as-is, commit as-is 
Git will not perform any conversions when checking out or committing text files. Choosing this option is not recommended for cross-platform projects("core. autocrif"is set to "false").
按原样签出,按原样提交 
在签出或提交文本文件时,Git不会进行任何转换。对于跨平台的项目,不建议选择这个选项("core. autocrif "设置为 "false")。

如不熟悉,请默认第一个选项,然后单击Next进入下一步。
11. 配置终端模拟器以与Git Bash一起使用,每个选项翻译如下:

Use MinTTY(the default terminal of MSYS2) 
Git Bash will use MinTTY as terminal emulator, which sports a resizable window, non-rectangular selections and a Unicode font. Windows console programs(such as interactive Python) must be launched via 'winpty' to work in MinTTY.
使用 MinTTY (MSYS2 的默认终端) 
Git Bash 将使用 MinTTY 作为终端模拟器,它具有可调整大小的窗口、非矩形的选择和 Unicode 字体。Windows 控制台程序(如交互式 Python)必须通过 "winpty "启动才能在 MinTTY 中工作。

Use Windows' default console window
Git will use the default console window of Windows ("cmd.exe"), which works well with Win32 console programs such as interactive Python or node.js, but has a very limited default scroll-back, needs to be configured to use a Unicode font in order to display non-ASCII characters correctly, and prior to Windows 10 its window was not freely resizable and it only allowed rectangular text selections.
使用Windows的默认控制台窗口
Git 将使用 Windows 默认的控制台窗口("cmd.exe"),该窗口在 Win32 控制台程序(如交互式 Python 或 node.js)中运行良好,但其默认的回滚功能非常有限,需要配置为使用 Unicode 字体才能正确显示非 ASCII 字符,并且在 Windows 10 之前,其窗口不能自由调整大小,只允许选择矩形文本。

如不熟悉,请默认第一个选项,然后单击Next进入下一步。
12. 选择"git pull"的默认行为(存疑,待求证)

  • 第一个相当于git fetch + git merge。
  • 第二个相当于git fetch + git rebase。
  • 第三个相当于git fetch + git rebase --ff-only。

如不熟悉,请默认第一个选项,然后单击Next进入下一步。
13. 选择凭证帮助程序,第一个为使用Git凭证管理,第二个为不使用,默认选择第一个选项,然后单击Next进入下一步。
14. 配置额外的选项,第一个为启用文件系统缓存,第二个为启用符号链接,默认仅选择第一个选项,然后单击Next进入下一步。
15. 配置实验性选项,第一个为启用对伪控制台的实验性支持,第二个为启用实验性的内置文件系统监视器,可能会有BUG,默认不选择,然后单击Install进行安装。
16. 安装完毕后,有两个选项,分别为:启动Git Bash与查看发行说明,可勾选也可不勾选,然后单击Finish完成安装。
17. 验证安装,在cmd或其他终端内输入git --version或者git -v,输出类似于git version 2.39.1.windows.1的内容,证明初步安装正常且PATH正常添加读取。

初步配置Git

版本控制系统需要知道是谁做的操作,所以Git需要你的一些信息,来用于提交,在cmd内输入(注意,请将名称与邮箱换成你需要的,这些信息可能会在后面用于Github或者Gitee):

git config --global user.name "GOOD-AN"
git config --global user.email "ganwjt@163.com"

注意:--global为全局设置,用于每个存储库,如只想为单存储库使用,则删除--global
至此,Git安装配置完毕,我们的学习之旅正式开启。

Git基本使用

本节中,我们将从新建本地存储库开始,逐步熟悉Git的一些基本指令。
本节中,所有指令仅涉及本地存储库,远端存储库的相关操作将在下一节中说明。
从本节开始,查看所有内容,如果最后一行为一个冒号,表示还有更多信息可以查看,如果不需要,直接按q即可退出。

注意:从此开始,我们建议所有命令不要复制粘贴,很容易造成学后前忘,会极大拖慢学习速度。

新建本地存储库

请找到一个合适的位置,新建一个文件夹,命名为想为这个存储库起的名字,比如命名为mylearngit,并进入这个文件夹。
为避免可能发生的问题,路径与文件夹名称尽量为英文。
如果你已经有了一个想用于Git的文件夹,可以直接打开。
我们在这个文件夹内启动cmd或者右键并选择Git Bash here(如果有)。
从此开始,演示都使用Git Bash。

Git初始化

在Git Bash内,输入命令git init,当输出类似Initialized empty Git repository in 文件夹路径/.git/时,恭喜你,你成功创建了第一个Git存储库。
Git新建了一个隐藏文件夹.git,关于这个存储库的一切信息都将存储在这个文件夹内,并且,Git将监听你初始化的文件夹,此文件夹就是工作区,从此开始,如无特殊说明,我们都将这类文件夹成为工作区。

添加第一个文件

刚刚,我们创建了一个本地存储库,但是它还是空空荡荡,我们可以添加或者新建一些文件。
例如,新建一个名为readme.txt的文本文档,并在里面写入This is the first file of my first repository.,保存并关闭。注意,请避免使用Windows自带记事本编辑。
返回到Git Bash中,使用git status查看存储库的状态,输出:

On branch master

No commits yet

Untracked files:
  (use "git add ..." to include in what will be committed)
        readme.txt

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

现在,Git知道有这个文件,但是并没有添加到存储库中,在Git存储库中,文件会有两种不同的大状态:

  • Tracked(追踪的):该文件Git知道且已被添加到存储库
  • Untracked(未被追踪的):该文件Git知道且未被添加到存储库

我们也可以使用git status --short或者git status -s来输出精简的信息:

?? readme.txt

最前面的符号表示该文件的状态,详细说明请见参考--文件状态。
所有的文件第一次放置到工作区内,都是处于Untracked状态下,我们需要将它们添加到暂存区中。

Git暂存

在做开发时,我们会添加、删除或修改文件,每次完成后,都应将变化的文件添加到暂存区中,使用命令git add,后面添加改变的文件或者目录名称,比如git add readme.txt,可添加多个,名称用空格分开,可以使用通配符。
如果需要将工作区内所有文件一次添加,请使用命令git add --allgit add -A
添加完成后,我们再次查看存储库状态,输出例如:

A readme.txt

当添加到暂存区后,我们就可以进行第一次提交了。

Git提交

添加提交后,Git保存这一时间点的所有变化,如果发现错误或者需要修改,可以很轻松的返回到保存的时间点上。

默认提交

使用命令git commit -m "Initial Submission",输出:

[master (root-commit) d4f7d3b] Initial Submission
 1 file changed, 1 insertion(+)
 create mode 100644 readme.txt

该命令执行提交,并添加了一条信息Initial Submission,这个信息用于概括此次提交的更改内容,被称为subject。
如果我们需要写入很多信息,直接使用命令git commit,Git将使用设置的编辑器打开提交信息文件,'#'号为注释,Git将忽略这些信息,在最下面输入信息即可,每行代表一条,首行一般为主标题,输入完毕后,保存并关闭,信息一定要简洁明了,让自己或他人可以很快的了解到修改的内容。
提交后,我们就可以使用命令git log查看所有的提交信息,输出例如:

commit d4f7d3b02a8de185db22823df3fa64bd0787d675 (HEAD -> master)
Author: GOOD-AN 
Date:   Wed Feb 8 21:11:01 2023 +0800

    Initial Submission

显示的内容从上到下分别为:提交的commit hash、作者、时间与信息。
提交时,如果非常确定修改内容,我们可以直接使用-a参数来跳过暂存区,例如:git commit -a -m "Add hello_world.cpp file"或者git commit -am "Add hello_world.cpp file"

注意:跳过暂存区,有时会提交并不想发生的改变,所以一般不建议跳过暂存区。

修改提交

如果我们发现提交信息写错了,或者提交的内容出错了,是有改正机会的,我们只需要在提交时添加参数--amend即可。
现在我们修改"readme.txt",在最后一行随意加一些内容,然后提交git commit -m "moidfy teh reddme.text",这时候我们查看log,很明显,提交信息上有很严重的错误,我们先记下这里的commit hash,用于接下来的比对,本例中为484cb9fabab0c9cf075e74a02f1be6c28a7988a0,退出,这时候我们直接重新提交,使用命令git commit --amend -m "modify the readme.txt",然后查看log,我们会发现,错误的提交已经消失,commit hash也变更了,本例中已修改为088784857eadbb342a8c926aa63843c00c1dd0ed
如果我们需要改正文件,那么不可直接提交,需重新暂存后提交或者提交时使用参数-a,此处可暂停尝试。
以上例子中,很容易明白这个添加这个参数提交时,不是传统意义上的覆盖,而是创建了一个新提交,替换的最后一次的提交。

注意:这样修改提交历史可能会带来不可估量的后果,通常本地来说没有关系,避免在远程存储库中使用,尤其是与他人协作的情况下。

Git标签

Git标签,也是一个标记某一个版本时刻的东西,一般用于标记一个重要的时刻点,比如这个项目有了一个重大更新,有人可能会说,不是已经有commit hash了吗,为什么还要标签呢,这样想,比如有人问你,那个最新版本是哪个?你说,就是那个"d4f7d3b02a8de185...",如果这次过后,又有很多提交,是不是找起来头都大了,那么换成tag呢,直接说,就版本号为v3.2的那个,那么我们直接找tag为v3.2的,是不是就容易多了呢。

创建标签

Git标签使用命令git tag来实现,分为轻量级和注释两种,使用区别就是注释要加参数-a,带注释的标签会作为完整的对象存储于Git存储库中,包括时间,提交人信息等等,轻量级标签就不会有这些信息,我们建议使用带注释的标签,除非有特殊原因,不得不使用轻量级标签。
如果使用参数-a,即带注释的标签,就可以类似git commit操作,带-m参数或者不带,不带会启动编辑器编辑标签说明,完整命令比如git tag -a v1.0 -m "First Release",或者git tag -a v1.0启动编辑器编写更详细的说明。
创建标签时会默认给当前最新的提交打上标签,查看日志,即可看到提交上的tag。

注意:标签是不可重复的。

标记标签

比如说之前某一次提交忘记打标签了,也可以事后添加,只需要在执行命令时加上commit hash即可,commit hash过长,取前7位即可,完整命令比如git tag -a v1.0 153ab16,然后再次查看日志,即可看到。

查看标签

使用命令git tag可以列出所有的已创建标签,使用命令git show tag_name即可查看某一标签的详细内容。

删除标签

如果发现标签错误的打在了某一个提交上,也可以删除,只需使用参数-d即可,完整命令比如git tag -d v1.0,查看已创建标签与日志均可发现已删除。

Git分支

分支也是版本控制非常重要的东西,一般用户都会使用主分支的内容,但是如果我们需要添加或修改一些功能,而这些添加或修改的功能还需要一定时间的编写与调试,那么我们贸然对主分支的内容进行修改,很容易造成主分支功能不可用,此时我们就可以创建一个分支,在分支上进行添加或修改,在全部开发完成后,合并到主分支上。

创建分支

我们使用命令git branch (branch name)来创建分支,例如git branch learn_branch
然后我们通过命令git branch查看当前存储库所有的分支,输出例如:

  learn_branch
* master

现在我们有了一个全新的分支,名为"learn_branch",但是master分支旁边的"*"号表明我们依旧在master分支上,我们需要切换分支。

注意:新建分支时,是以当前所在分支节点创建的,故在创建新分支前请仔细查看当前分支信息。

切换分支

我们使用命令git checkout (branch name)来切换分支,例如git checkout learn_branch,输出Switched to branch 'learn_branch',即表明切换成功,我们可以再次通过命令git branch来查看。
以上新建与切换分支,可以直接使用命令git checkout -b (branch name),一步完成。

修改分支

类似之前的教程一样,我们添加一个文件"hello_world.cpp",写入内容:

#include <iostream>
using namespace std;

int main()
{
   cout << "Hello World";
   return 0;
}

修改"readme.txt",添加一句话:First experience with branch。
修改完成后,将文件添加到暂存区,git add -A,查看存储库状态,输出:

A  hello_world.cpp
M  readme.txt

提交到新分支,git commit -m "Add hello_world.cpp file",输出:

[learn_branch 0f8f80a] Add hello_world.cpp file
 2 files changed, 10 insertions(+), 1 deletion(-)
 create mode 100644 hello_world.cpp

分支间的切换

现在,让我们使用checkout切换到主分支上,我们将注意到,工作区内的文件都变回主分支的状态,甚至新建的文件也消失不见,不用担心,那些文件并没有真正的消失,它们都被Git记录存储下来了,不管分支有多少,也不管分支怎么变,Git始终会令当前显示的文件和文件内容与选择的分支内容一致。

合并分支

首先,我们需要切换分支到需要接受合并的分支上,比如本章为"master",然后我们使用命令git merge (branch name)合并分支,本章中为git merge learn_branch,输出:

Updating d4f7d3b..0f8f80a
Fast-forward
 hello_world.cpp | 8 ++++++++
 readme.txt      | 3 ++-
 2 files changed, 10 insertions(+), 1 deletion(-)
 create mode 100644 hello_world.cpp

即合并成功,现在我们可以删除这个不需要的分支了。

删除分支

使用命令git branch -d (branch name),本章中为git branch -d learn_branch,输出:Deleted branch learn_branch (was 0f8f80a).,即为删除成功。

合并冲突

在以上的合并中,分支来自"master",且"master"并未做任何修改,所以Git会认为"master"是"learn_branch"的直接祖先,这里就属于称为"Fast-forward branch (快进合并)"的合并类型,那么假设在创建分支后,同时修改的主分支与新分支,那么再次合并时会发生什么呢?让我们来一起尝试一下。
这里我们新建并切换分支git checkout -b learn_conflict,然后修改新分支下"readme.txt"文件的内容,最后加上一行:First time branch conflict resolution,保存关闭后,提交修改git commit -am 'changed the readme.txt'
切换回主分支git checkout master,再次修改"readme.txt"文件的内容,最后一行加上:Modify master branch,保存关闭后,提交修改git commit -am 'changed the readme.txt in the master branch'
全部修改提交完毕后,我们来尝试合并分支,"master"依旧为接受合并的分支,使用命令git merge learn_conflict合并分支,输出:

Auto-merging readme.txt
CONFLICT (content): Merge conflict in readme.txt
Automatic merge failed; fix conflicts and then commit the result.
自动合并 readme.txt
冲突 (内容):readme.txt中的合并冲突
自动合并失败;修复冲突,然后提交结果。

我们发现合并失败,如果这时候查看存储库状态,输出:UU readme.txt,即两个分支的"readme.txt"均做了修改,那么我们就要修复这个冲突。

修复合并冲突

在编辑器中打开"readme.txt",会发现内容如下:

This is the first file of my first repository.
First experience with branch.
<<<<<<< HEAD
Modify master branch.
=======
First time branch conflict resolution.
>>>>>>> learn_conflict

Git用<<<<<<<=======>>>>>>>来标记冲突内容,如何修改,请按实际情况处理,比如这里把"master"分支的"readme.txt",该段修改为:First time branch conflict resolution.,然后保存关闭。
将冲突的文件重新添加进暂存区,git add readme.txt,重新检查状态,输出:M readme.txt
提交,git commit -m "merge branch learn_conflict",删除无用分支git branch -d learn_conflict
现在冲突已经解决完毕,并且成功合并。

Git撤销

Git还原

Git还原,指做一个新的提交,恢复其他提交的改动。
举个例子,比如有三次提交,分别顺次提交了"a.txt"、"b.txt"与"c.txt",现在我们发现不需要"b.txt"这次提交,我们就可以使用Git还原,处理完毕后,将多出来一次提交,并且文件只剩下"a.txt"与"c.txt"。
接下来,实际尝试一下,我们首先删除一个文件,然后提交git commit -m "Try Revert",接下来,我们来回到过去,首先,我们需要找到我们需要返回的点,所以我们来查阅日志,之前做过的提交有很多,信息比较难找,所以这次我们添加参数--oneline,即git log --oneline,这将显示commit hash的前七位以及提交信息,例如输出:

8e27837 (HEAD -> master) Try Revert
8d847fb modify the readme.txt
8d0edb9 merge branch learn_conflict
a68195e changed the readme.txt in the master branch
153ab16 changed the readme.txt
0f8f80a Add hello_world.cpp file
d4f7d3b Initial Submission

现在我们想撤回"8e27837 (HEAD -> master) Try Revert"的更改,即删除文件的操作,我们使用命令git revert HEAD --no-edit来处理,

HEAD是Git对当前版本的标识,HEAD^代表上一个版本,每增加一个"^"号,就往上多推一个版本,当然,如果我们需要回退几十上百个版本,几十上百个"^"号数起来也比较困难,所以我们可以使用HEAD~x,"x"即回退的版本数,"~"号不可省略,但是往往我们无法确定需要回退的版本数量,我们可以使用commit hash直接替换HEAD。
--no-edit表示不打开提交内容编辑,使用默认信息提交,如果需要自定义内容,删除该参数即可。
因也属于提交,故该命令输出内容与commit一致,完成后,我们会发现,删除的文件已恢复,输出日志查看,会多出一条记录:618dffb (HEAD -> master) Revert "Try Revert",即还原操作的记录。

Git重置

Git重置,指回到过去某一提交,并丢弃这一提交(不包括该提交)后面所有的提交,这将改变提交记录。
上一小节中,我们使用Git还原来回溯一些修改,这个操作也可以通过重置来实现,同样的,我们需要找到我们需要返回的点,所以首先来查阅日志,输出类似:

618dffb (HEAD -> master) Revert "Try Revert"
521ab3e Try Revert
8d847fb modify the readme.txt
8d0edb9 merge branch learn_conflict
a68195e changed the readme.txt in the master branch
153ab16 changed the readme.txt
0f8f80a Add hello_world.cpp file
d4f7d3b Initial Submission

比如这里我们知道"521ab3e Try Revert"这个提交删除了文件,那么我们想回到未删除的状态,就可以重置到"8d847fb modify the readme.txt"这个提交,与git revert命令类似,将revert替换为reset即可,只不过这里没有新提交,所以没有提交日志,不需要参数--no-edit
开始前先记录一下日志信息,备用。
本文章使用commit hash来演示,使用命令git reset 8d847fb,如果一切顺利,是没有任何输出的,我们通过日志来查看,输出类似:

8d847fb (HEAD -> master) modify the readme.txt
8d0edb9 merge branch learn_conflict
a68195e changed the readme.txt in the master branch
153ab16 changed the readme.txt
0f8f80a Add hello_world.cpp file
d4f7d3b Initial Submission

我们发现,先前的记录已看不见,重置成功,那么假设说,重置出错了,我们需要撤销重置,也是可以的,但是,我们要知道提交的commit hash,使用同样的命令,比如,回到"521ab3e Try Revert"这次提交,使用命令git reset 521ab3e,然后查阅日志,我们会发现提交恢复了,虽然文件没有删除,但是如果检查状态的话,会发现删除的文件已经处于未跟踪状态了。
现在我们可以明白,这个记录实际上并没有真正意义上的删除,仅仅是不显示了而已,另一点,git reset默认操作保留已修改的文件,但不标记为提交,所以已经删除的文件,并不会在工作区中消失,而是变为未跟踪。

Git恢复

Git恢复指从索引或其他提交中恢复工作树上的文件,这个命令不会更新你的分支。
1.撤销工作区的修改
如果我们修改了一些文件,还没有提交,但是我们发现修改并不需要,而手动恢复又会很麻烦,就可以使用命令git restore,例如我们修改"readme.txt"的文件内容,保存后使用命令git restore readme.txt,可以发现文件恢复到修改前的状态了。
2.撤销暂存区的修改
我们修改"readme.txt"的文件内容,并将其添加进暂存区,这时候我们发现这个修改并不是我们想要的,所以我们需要撤销,这时候就可以使用命令git restore --staged readme.txt或者git restore -S readme.txt,然后我们来查看存储库状态,该文件已经恢复到添加暂存区前的状态了。
3.从指定分支中恢复文件修改
我们修改"readme.txt"的文件内容,然后查看提交日志,输出例如:

8d847fb (HEAD -> master) modify the readme.txt
8d0edb9 merge branch learn_conflict
a68195e changed the readme.txt in the master branch
153ab16 changed the readme.txt
0f8f80a Add hello_world.cpp file
d4f7d3b Initial Submission

如果想恢复到某一提交分支,可以使用命令git restore --source HEAD readme.txt或者git restore -s HEAD readme.txt,这里HEAD的理解与Git重置一节一致,所以也可以使用commit hash来替换,运行完毕后,查看文件已经变为指定分支的内容。
此命令我们都要注意一点,除了--staged是修改暂存区的以外,其余都是对工作区的修改,是不会直接改变存储库的内容的。

依据官方说明,git restore还处于实验阶段,命令可能随时修改,请关注你使用版本的帮助文档说明。

其他

本节中,主要对一些可能常用的命令进行说明

查看文件差异

1.直接使用命令git diff,可查看暂存区中与工作区的文件差异。
2.添加参数--cached--staged,可查看暂存区与上一次提交的文件差异。
3.添加分支名称可查看两个分支间的文件差异。
4.添加commit hash可查看两次提交间的文件差异,可套用之前的HEAD说明。
5.添加参数--name-only,可仅查看差异文件名称。
以上命令中可添加文件名称,单独查看某些文件差异。

查看提交记录

从之前的学习中已经了解到了,查看日志的命令是git log,使用--oneline参数来简化输出,这里补充一些其他的参数。
1.使用参数--author author_name指定查看某个作者的提交记录。
2.使用参数--graph查看提交出现的分支与合并。
3.使用参数--max-count number或者-n number或者-number来限制输出的记录数量。
4.使用参数--reverse来反向顺序输出提交记录。
5.使用参数--since data或者--after data来输出指定日期之后的提交。
6.使用参数--until data或者--before data来输出指定日期之前的提交。

查看文件提交记录

以下所有命令必须最后添加文件名(有且只有一个)。
1.直接使用命令git blame,输出为:提交commit hash、提交作者名、提交时间、提交信息。
2.添加参数-L start-line,end-line,可查看start-line到end-line的指定信息,例如git blame -L 2,5,即查看第2至5行(包含)的信息;git blame -L 2,+3,即查看从第2行开始的3行输出。
3.添加参数--show-email或者-e,将显示提交作者邮件,而不是名称。

删除文件

以下所有命令必须最后添加文件名。
1.直接使用命令git rm,可将工作区与暂存区文件一起删除。
2.添加参数-f,将执行强制删除,在遇到例如文件已添加暂存区,而工作区已删除的情况下,可用git add -A等命令达到类似效果。
3.添加参数--cached,可仅删除暂存区文件,保留工作区文件。

移动文件

如果直接重命名文件,会导致Git认为删除了一个文件,又添加了一个文件,所有跟踪记录会丢失,所以可以使用移动文件命令。
以下所有命令必须最后添加文件名(有且只有两个)。
1.直接使用命令git mv,默认效果。
2.添加参数--force-f,强制重命名或移动文件,即使目标存在。
3.添加参数-k,跳过会导致错误条件的移动或重命名操作。
4.添加参数--dry-run-n,只显示会发生什么,什么都不会做。
5.添加参数--verbose-v,在移动文件时显示文件的名称。

查看引用日志

引用日志(reference logs),记录了你的在本地存储库做出的一切修改,由Git维护,并使用命令git reflog列出。
1.默认命令git reflog,等同于git reflog show,即在执行git log -g --abbrev-commit --pretty=oneline
2.命令git reflog expire,删除过期的日志,这通常不由终端用户直接使用。
3.命令git reflog delete,从引用日志中删除单个条目,其参数必须是确切的条目,例如git reflog delete master@{2}
4.命令git reflog exists,检查否有一个reflog。
此命令更详尽说明计划于下一篇文章中写明。

帮助

如果在使用时需要问题,可以首先查阅文档,查阅文档有两种方式,一个是直接去官方网站查看。
另一个就是使用命令git command -help或者git command --help,前者将在命令框中输出帮助指令,后者将在浏览器打开本地的帮助文档。
我们也可以使用命令git hepl -a来浏览所有命令。

当前我们一切使用都是在本地运行,如果确保基础命令都已掌握,那么接下来我们就该学习如何使用远程存储库了。

即将进入远程存储库的学习,请务必养成所有改动提交前检查文件是否包含敏感信息的习惯,例如账密、Cookie等,信息无价,切莫泄露!

远程存储库

Git并没有一个中心服务器,到目前为止,所有操作均在本地,如果想分享你的项目或者与他人合作开发,你就需要将你项目的所有数据放置于一个其他人员能连接的服务器上,托管平台有很多,比如GithubGitLabBitbucketGiteeGitea等等,本章将使用Github来说明,如果无法连接,可尝试配合Watt Toolkit开源工具箱内github工具使用。

创建账户与存储库

转到Github,注册并登录一个账户,请注意,注册邮件请使用之前Git设置的邮箱,用户名尽量一致吧,按照官方步骤注册完毕进入主页。
进入后,点击右上角加号,新建存储库。
【图片】
输入存储库名称,这里的所有选项我们将在后续说明,现在,只需选择Public(每个人都可访问)或者Private(可以指定谁能访问),然后单击"Create repository"。
【图片】

推送到Github

我们在之前的学习中,已经建立了一个本地存储库,如果你想,可以重新建立一个本地存储库,点击https,然后复制显示的URL。
【图片】
然后在本地Git Bash中使用命令git remote add origin https://github.com/GOOD-AN/hello-world.git,请使用你自己的URL更换演示命令中的URL,该命令将指定一个远程存储库,地址为输入的URL,origin为本地存储库对远程存储库的别名,可依据实际需要更换。
现在我们使用命令git push --set-upstream origin master或者git push -u origin master将"master"分支推送到远程存储库,--set-upstream参数意义是将本地"master"分支与远程"master"分支创建一个跟踪,建议永久的关联,这在首次推送新分支时很有用,后续这个参数可以不加。

由于是第一次使用Git连接,Github需要验证你的身份,请根据弹出的窗口操作验证即可。
输出例如:

Enumerating objects: 16, done.
Counting objects: 100% (16/16), done.
Delta compression using up to 2 threads
Compressing objects: 100% (14/14), done.
Writing objects: 100% (16/16), 1.54 KiB | 787.00 KiB/s, done.
Total 16 (delta 4), reused 0 (delta 0), pack-reused 0
remote: Resolving deltas: 100% (4/4), done.
To https://github.com/GOOD-AN/hello-world.git
 * [new branch]      master -> master
branch 'master' set up to track 'origin/master'.

现在,回到Github,并刷新它,可以看到存储库已经被更新。
【图片】

Github编辑

Github除了可以作为存储外,还允许我们在线编辑上传的文件,我们看到readme.txt的内容已经显示在存储库首页,README文件主要用于说明有关项目的重要信息,比如项目的用途、如何使用或者如何参与项目等等,更推荐使用Markdown格式,即扩展名为.md

现在让我们单击右边的编辑按钮,然后将文件名重命名为"README.md",并修改内容为

# Hello World
This repository is used to learn the use of Git and Github.

For more information, please visit: https://blog.goodant.top/git-starter-tutorial/

【图片】
修改完毕后,最下面可以输入你的提交信息,不输入默认也可,然后单击最下方的"Commit changes"。
【图片】
等待跳转后,修改完成。

Git拉取

一般当维护一个远程存储库时,每次工作前,我们都要确保本地文件保持最新,在Git中,使用git pull来完成,git pull就是将将远程版本库的修改并入当前分支,工作流程是先根据给定的参数调用git fetch,然后根据参数与配置选择调用git rebasegit merge来处理出现分歧的分支。

从此开始的命令中,如无特殊说明,如果不需要选定远程分支来源,直接使用默认来源,可以省略origin处的参数。

Git Fetch

fetch用于从远程存储库下载内容(从一个或多个其它存储库获取分支和/或标记(统称为"refs"),以及完成其历史所需的对象)。
让我们在本地尝试一下,使用命令git fetch origin,输出:

remote: Enumerating objects: 4, done.
remote: Counting objects: 100% (4/4), done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), 804 bytes | 89.00 KiB/s, done.
From https://github.com/GOOD-AN/hello-world
   8d0edb9..5247f03  master     -> origin/master

然后我们来查看一下存储库状态,注意输出完整信息,输出:

On branch master
Your branch is behind 'origin/master' by 1 commit, and can be fast-forwarded.
  (use "git pull" to update your local branch)

nothing to commit, working tree clean

发现提示"你的分支落后'origin/master'1次提交,并且可以快进",那么我们来看看有哪些变化,使用命令git diff origin/master,输出:

diff --git a/README.md b/README.md
deleted file mode 100644
index fc87bdc..0000000
--- a/README.md
+++ /dev/null
@@ -1,4 +0,0 @@
-# Hello World
-This repository is used to learn the use of Git and Github.
-
-For more information, please visit: https://blog.goodant.top/git-starter-tutorial/
diff --git a/readme.txt b/readme.txt
new file mode 100644
index 0000000..f7387e6
--- /dev/null
+++ b/readme.txt
@@ -0,0 +1,3 @@
+This is the first file of my first repository.
+First experience with branch.
+First time branch conflict resolution.

那么我们可以确定,这是我们刚刚Github编辑的结果,现在,我们可以合并它了。

Git Merge

Git Merge是一个把两个或多个开发分支合并成一个的命令,用于整合来自不同来源或合作者的修改。
刚刚我们确定了这些更新是我们需要的,那么使用命令git merge origin/master来合并,输出:

Updating 8d0edb9..5247f03
Fast-forward
 README.md  | 4 ++++
 readme.txt | 3 ---
 2 files changed, 4 insertions(+), 3 deletions(-)
 create mode 100644 README.md
 delete mode 100644 readme.txt

然后我们来查看一下存储库状态,输出:

On branch master
Your branch is up to date with 'origin/master'.

nothing to commit, working tree clean

好了,现在本地分支的内容已经是最新的了。

Git Pull

那么如果我们非常确定这个更新,不想很麻烦的使用以上那些命令查看变更了,那么我们可以直接用git pull来处理。
让我们在Github做一些修改后提交,然后本地使用命令git pull origin,输出:

remote: Enumerating objects: 5, done.
remote: Counting objects: 100% (5/5), done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 3 (delta 1), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), 718 bytes | 119.00 KiB/s, done.
From https://github.com/GOOD-AN/hello-world
   5247f03..8ce792b  master     -> origin/master
Updating 5247f03..8ce792b
Fast-forward
 README.md | 2 ++
 1 file changed, 2 insertions(+)

这就是如何使用远程存储库数据来保持本地存储库数据最新的方法,git rebase因在默认设置中未使用,故将在下一篇文章中介绍。

Git推送

让我们来新建一个名为"multi_table.cpp"的文件,添加内容:

#include <iostream>
#include <iomanip>

int main()
{
    int i, j, result;
    for (i = 1; i < 10; i++)
    {
        for (j = 1; j <= i; j++)
        {
            result = i * j;
            std::cout << i << "*" << j << "=" << std::left << std::setw(3) << result << " ";
        }
        std::cout << "\n";
    }
}

添加到暂存区后提交git commit -m "add multi_table.cpp",查看存储库状态,输出:

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

证明我们已经有一个提交在'origin/master'之前了,使用命令git push origin推送,推送完毕后,即可返回Github查看。

Github分支

本节我们将学习如何在Github新建分支并拉取到本地使用,本地新建分支如何推送到Github上。

Github新建分支

返回到Github存储库中,单击分支标签,输入新的分支名称,然后单击创建分支。
【图片】
等待浏览器刷新后,现在应该已经成功创建分支并且切换到了新分支上。
我们选择一个文件,然后单击编辑,编辑完成后,单击上方的Preview changes,查看做得修改,红色代表修改的行,绿色代表修改后的新内容,确认无误后,输入提交信息后提交修改。
【图片】

Git拉取分支

首先我们在本地拉取同步,输出:

remote: Enumerating objects: 5, done.
remote: Counting objects: 100% (5/5), done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 3 (delta 2), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), 715 bytes | 79.00 KiB/s, done.
From https://github.com/GOOD-AN/hello-world
 * [new branch]      format_error -> origin/format_error
Already up to date.

可以发现多了一个新分支,我们使用git branch -a查看本地与远程的所有分支,输出:

* master
  remotes/origin/format_error
  remotes/origin/master

后两个为远程分支,发现本地并没有新分支的出现,因为Git默认不会直接建立跟踪关系,我们使用命令git checkout format_error,输出:

Switched to a new branch 'format_error'
branch 'format_error' set up to track 'origin/format_error'.

现在已经切换为新分支并建立了跟踪关系,使用git branch -a查看,输出:

* format_error
  master
  remotes/origin/format_error
  remotes/origin/master

现在我们可以查看本地工作区的文件,不出意外的话,文件已经更换为新分支的文件。

Git推送分支

首先我们来新建一个分支git checkout -b update_readme,然后修改该分支的"README.md",修改完毕后提交git commit -m "Updated README for update_readme branche",现在我们使用命令git push origin update_readme将新分支推送到Github上,输出:

Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 2 threads
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 453 bytes | 151.00 KiB/s, done.
Total 3 (delta 1), reused 0 (delta 0), pack-reused 0
remote: Resolving deltas: 100% (1/1), completed with 1 local object.
remote:
remote: Create a pull request for 'update_readme' on GitHub by visiting:
remote:      https://github.com/GOOD-AN/hello-world/pull/new/update_readme
remote:
To https://github.com/GOOD-AN/hello-world.git
 * [new branch]      update_readme -> update_readme

推送成功后,返回Github查看,确认新分支。
【图片】

Github合并分支

Github上允许我们查看分支的修改并将分支合并。
现在我们单击"Compare & pull request"。
【图片】
然后在最下方就可以看到文件的变更。
【图片】
如果确定变更无误,在上方输入信息,并单击"Create pull request"来创建一个拉取请求。
【图片】
Github将跳转到一个审查页面,在这个页面,你可以与合作者讨论和审查这些修改,并且可以在合并前继续新的修改操作,等待合并检查无误后,单击"Merge pull request"进行合并,成功后将显示如下页面。
【图片】
为避免存储库变得冗杂,可以单击"Delete branch"来删除合并后不再使用的分支。
现在让我们回到项目首页,单击查看分支。
【图片】
在确定另一个分支的变动已经被包括在内后,将其删除。
【图片】

Github协作

Git核心是协作,但是Git不允许在没有权限的情况下向其他存储库提交代码,但是,我们可以复制一份存储库,然后修改内容,之后将这些改动提交到原始存储库。

Fork存储库

fork就是复制一个存储库的副本,fork并不是Git提供的操作,而是Github及其类似的托管服务器提供的,让我们到Github上,fork存储库https://github.com/GOODADNT/hello-world
【图片】
现在,我们拥有了自己的存储库副本。
【图片】

Clone存储库

当前存储库都是在Github上,如果我们想在本地使用与修改它们,那么就可以克隆一份到本地。
对于一个多人协作的存储库来说,很有可能在你修改Fork后的存储库文件时,原始存储库(即上游)文件被修改了,导致在拉取请求时,出现冲突,审查无法通过,这时候我们就需要更新本地文件,我们可以在Github上同步后本地拉取更新,但是这样就显得比较麻烦,还记得之前学习远程存储库时,我们知道可以为远程存储库设置别名,意味着我们可以设置多个远程存储库,那么就可以从原始存储库拉取更新,将本地修改推送到Fork后自己的存储库。
现在,让我们复制Fork后存储库URL,然后使用命令git clone https://github.com/GOOD-AN/hello-world.git克隆到本地,如果需要自定义本地文件夹名称,在URL后添加名称即可,注意有空格。输出:

Cloning into 'hello-world'...
remote: Enumerating objects: 32, done.
remote: Counting objects: 100% (32/32), done.
remote: Compressing objects: 100% (21/21), done.
remote: Total 32 (delta 9), reused 32 (delta 9), pack-reused 0
Receiving objects: 100% (32/32), 4.99 KiB | 2.50 MiB/s, done.
Resolving deltas: 100% (9/9), done.

然后我们进入这个文件夹内,可以像之前一样,查看这个存储库的状态、提交记录等等。

配置远程地址

现在,让我们看这个本地存储库的远程配置,使用命令code>git remote -v,输出:

origin  https://github.com/GOOD-AN/hello-world.git (fetch)
origin  https://github.com/GOOD-AN/hello-world.git (push)

我们可以看到,现在仅仅有Fork后的存储库地址,而我们也想添加原始的存储库地址,使用命令git remote add upstream https://github.com/GOODADNT/hello-world.git,我们将远程存储库别名设置为"upstream"。

根据Git的命名规范,origin应设置为你自己的存储库,upstream设置为原始的存储库,当然,这是自由的,你可以设置自己喜欢的别名。
然后我们来查看现在的远程配置,输出:

origin  https://github.com/GOOD-AN/hello-world.git (fetch)
origin  https://github.com/GOOD-AN/hello-world.git (push)
upstream        https://github.com/GOODADNT/hello-world.git (fetch)
upstream        https://github.com/GOODADNT/hello-world.git (push)

现在我们拥有了两个远程地址,origin拥有读写的全部权限,upstream仅仅只有读的权限。

做出贡献

让我们来在本地存储库修改和(或)添加一些文件,提交后推送到我们Fork的远程存储库中git push origin,然后我们返回到Github中查看,可以看到有了一个新提交,并且可以向原始存储库打开一个拉取请求。
【图片】
确认存储库与分支正确,填写说明信息后,创建拉取请求。
【图片】
现在所有拥有权限的用户都可以查看这个请求,等待审查通过,管理员批准后,这个请求将合并,你就成功完整的为存储库做出了一次贡献。

批准请求

如果你拥有这个存储库的权限,将在"Pull requests"看到所有拉取请求。
【图片】
点进任意一个请求,即可查看说明与变动,还可参与讨论,如果有权限,你可以批准或关闭该请求。
【图片】
你可以尝试这里面的所有操作。

Git初进阶

到此,其实Git已经基本学习完毕了,下面的学习是为了让你使用起来更加的安全与便捷。

忽略文件

有些时候,你在开发项目时,会不得不把一些文件放到工作区中,比如账号密码的配置文件、打包文件、运行日志文件等等,但是又不能提交它们,又怕提交的时候出错,又觉得移除后续再添加很麻烦,很幸运,Git考虑到了这一点,我们只需要在项目内添加一个文件.gitignore,Git就会自动读取里面的配置,根据配置忽略文件。
首先我们在项目根目录新建.gitignore,如果出现强制输入文件名的情况,可以在文本编辑器中保存或者另存为,编译该文件,比如我们要忽略所有的日志文件(假设扩展名为.log)和打包生成的dist文件夹,一般来讲,一行即一条规则,那么我们就可以书写如下规则:

*.log
dist/

现在所有扩展名为.log的文件和dist文件夹包括里面的文件都将被Git忽略。

默认在根目录下的.gitignore适用于整个项目,每个子目录都可以拥有.gitignore文件,只是规则仅在该子目录下生效。
被忽略的文件在添加到暂存区时可以使用-f参数强制添加,一般仅用于临时特殊情况添加。
.gitignore书写规则与详细说明请查阅:参考-- .gitignore规则。

Git SSH

在之前的使用中,我们一直使用的是HTTPS,一般来讲,HTTPS已经足够,但是如果处于不安全网络或者安全要求性较高的环境下,应该使用SSH,也推荐日常将SSH设为默认。

什么是SSH

SSH即Secure Shell Protocol,是一种加密网络协议,用于在不安全的网络上安全地运行网络服务,一般用于身份验证。SSH密钥使用非对称加密,即拥有一个公钥和一个私钥,公钥是你与对方共享的密钥,私钥是你保存在一个安全地方的密钥。注意,SSH只是一个协议,加密由其他算法提供,比如DSA、RSA、ECDSA、ED25519等等。
身份验证中,对方将一段验证信息使用公钥加密后传给你,你用私钥解密,然后再传给他,他将你传回的信息与传给你的信息比对,成功后,即证明身份属于你。
更多信息参阅Wikipedia - Secure ShellWhat is SSH (Secure Shell)?

生成SSH密钥

从2022年3月15日起,Github已经移除了旧的、不安全的密钥类型,DSA将不再被支持,RSA密钥必须使用SHA-2签名算法。
现在让我们来到Git Bash内,Git已经内置了多种生成算法。

  1. 使用命令ssh-keygen -t ed25519 -C "your_email@example.com",这将使用你的电子邮箱作为标签。
    如果你的系统不支持Ed25519,请改为ssh-keygen -t rsa -b 4096 -C "your_email@example.com"
  2. 输入生成的文件名,如无特殊情况,直接按回车,默认即可。
  3. 输入一个安全口令用于加密密钥,需重复输入两次。
    安全口令可以保障他人访问你的计算机时无法获取密钥,但是每次使用前都必须输入验证。
  4. 将SSH密钥添加进ssh-agent,使用命令ssh-add ~/.ssh/id_ed25519,如果之前设置了安全口令,需要输入验证。

现在,SSH可以在Git中使用了。

Github添加密钥

之前我们说过,对方需要持有你的公钥,也就是Github需要知道你的公钥是什么。

  1. 使用命令clip < ~/.ssh/id_ed25519.pub复制公钥,此命令会将公钥直接复制到剪切板中。
    如果该命令无效,请找到生成时目录,然后用文本编辑器打开后复制。
  2. 进入Github账户设置,在左侧单击"SSH and GPG keys"。
  3. 单击右侧"New SSH key"
  4. Title输入你习惯的名称,Key type选择Authentication Key,然后将公钥复制到Key中,单击"Add SSH key"

测试SSH连接

  1. 使用命令ssh -T git@github.com
  2. 请确认输出的指纹信息与GitHub's SSH key fingerprints看到的一致,然后输入"yes"。

添加SSH远程地址

首先,我们回到Github的仓库中,在查看,复制地址的地方点击"ssh",然后复制地址。
【图片】
可以像之前一样直接添加,也可以使用命令git remote set-url remote_name ssh_url来修改,我们可以将两个地址都修改git remote set-url origin git@github.com:GOOD-AN/hello-world.gitgit remote set-url upstream git@github.com:GOODADNT/hello-world.git,然后查看配置,输出:

origin  git@github.com:GOOD-AN/hello-world.git (fetch)
origin  git@github.com:GOOD-AN/hello-world.git (push)
upstream        git@github.com:GOODADNT/hello-world.git (fetch)
upstream        git@github.com:GOODADNT/hello-world.git (push)

至此,本节SSH密钥学习结束。

Git别名

在熟悉了你能用到的几乎所有的操作之后,我们想来讲一个使你使用Git更加便捷、高效的功能————别名,如果熟悉Linux的别名,这里是相同的概念,如果不熟悉也没关系,下面我们会尽量解释明白这是做什么的,在之前我们使用命令都需要输入相对比较长的单词或者语句,在实际使用过程中,很有可能忘记或者输入错误,即使输入正确,也需要花费一定的时间,那么我们可以不可以只输入一部分,或者习惯的名称来指代一些命令呢?答案是可以的,就是我们本节要讲的别名。

设置别名

设置别名很简单,我们只需要一个简单命令git config --global alias.<name> <command>,参数--global与之前说明一致,例如查看存储库状态的status,我们就可以设置git config --global alias.st status,以后我们再使用时,就可以直接输入git st
当然,我们在使用命令是可能会加上参数,也可以一并写进去,例如之前在Git重置一节,我们知道git reset HEAD -- <file>可以将暂存区的文件撤销掉,那么我们可以设置git config --global alias.unstage 'reset HEAD --',使用时只需要写git unstage file即可。
那么如果说你想运行一个外部命令,而非Git的子命令,设置别名时可以在命令前加上感叹号,例如git config --global alias.visual '!gitk',即使用命令git visual,即可运行gitk。

个性化

本节将讲述一些可能令你使用或者观感更加舒适的设置或操作。

log个性化

这一节呢,我们想单拿出来一个有意思的命令,就是log,在我们之前打印日志的时候,不是过于简单,就是过于繁杂,那么我们就想让它可以依据自己的喜好来展示内容,很棒的一点是,官方已经想到了这一点,我们可以设置pretty参数来实现这一点。

format子项

pretty有很多子项,本节我们主要说明format:这个子项,该子项允许你指定想得到的信息,原理有些类似于printf(如果你学习过的话),
我们通过官方的例子来简单说明一下,完整命令为git log --color --pretty=format:'The author of %h was %an, %ar%nThe title was >>%s<<%n',输出:

The author of d540da0 was GOOD-AN, 5 days ago
The title was >>add pascal's triangle<<

这里的%h、%an、%ar等都称为之占位符,用来指代不同的内容,%h是简写的七位commit hash,%an是提交作者名称,%ar是相对作者的提交日期,%n是换行,%s是提交信息。
这里说明一下%ar与%cr的区别,即author date与committer date的区别,author date即最初提交日期,committer date即最终修改提交日期,在Git Book中写到:"You may be wondering what the difference is between author and committer. The author is the person who originally wrote the work, whereas the committer is the person who last applied the work. So, if you send in a patch to a project and one of the core members applies the patch, both of you get credit — you as the author, and the core member as the committer.",翻译过来就是:“你可能想知道作者和提交者之间有什么区别。作者是最初编写工作的人,而提交者是最后应用工作的人。因此,如果你向项目发送了一个补丁,而其中一个核心成员应用了这个补丁,那么你们两个人都会得到认可--你是作者,而那个核心成员是提交者。”
更多format的参数可参阅:参考-- format占位符

示例

通过各类参数,我们可以写出类似这样的命令,git log --graph --abbrev-commit --decorate --color --format=format:'%C(bold red)%h%Creset - %C(bold green)(%cr)%Creset %s %C(bold blue)<%an>%C(bold yellow)%d%Creset'
你将获得这样的输出:
【图片】
--graph在输出的左侧绘制基于文本的图形提交历史记录。
--abbrev-commit将缩短commit hash,还记得之前使用的--oneline参数吗,就是--pretty=oneline --abbrev-commit的简写
--decorate将显示的任何提交的引用名称,如HEAD、tag等等额外信息,如果你使用git的是大于v2.12.2的版本,这个参数将默认附带。
--color,显示颜色差异,默认附带。
--all这个参数示例并没有写,因为这个参数将显示所有分支的提交记录,一般来说不需要查看所有分支,如果需要,建议再写一个关于log的别名。
format参数就是颜色加要输出的内容,%C就是来设置颜色的,我们可以将其添加别名:

git config --global alias.lg "log --graph --abbrev-commit --decorate --color --format=format:'%C(bold red)%h%Creset - %C(bold green)(%cr)%Creset %s %C(bold blue)<%an>%C(bold yellow)%d%Creset'"
git config --global alias.lg2 "log --graph --abbrev-commit --decorate --color --format=format:'%C(bold red)%h%Creset - %C(bold green)(%cr)%Creset %s %C(bold blue)<%an>%C(bold yellow)%d%Creset' --all"

记得输入时别误添加git。

参考

文件状态

  • ??:未被追踪的
  • M:修改
  • T:文件类型改变
  • A:添加
  • D:删除
  • R:重命名
  • C:拷贝(如果配置选项 status.renames 被设置为 "拷贝")
  • U:更新但未合并
之间可以两个组合,组合后的说明见下表
X          Y     Meaning
-------------------------------------------------
	     [AMD]   not updated
M        [ MTD]  updated in index
T        [ MTD]  type changed in index
A        [ MTD]  added to index
D                deleted from index
R        [ MTD]  renamed in index
C        [ MTD]  copied in index
[MTARC]          index and work tree matches
[ MTARC]    M    work tree changed since index
[ MTARC]    T    type changed in work tree since index
[ MTARC]    D    deleted in work tree
	        R    renamed in work tree
	        C    copied in work tree
-------------------------------------------------
D           D    unmerged, both deleted
A           U    unmerged, added by us
U           D    unmerged, deleted by them
U           A    unmerged, added by them
D           U    unmerged, deleted by us
A           A    unmerged, both added
U           U    unmerged, both modified
-------------------------------------------------
?           ?    untracked
!           !    ignored
-------------------------------------------------

.gitignore规则

模式 说明 例子
# 注释内容 解释说明用,读取时将被忽略  
name.type 匹配所有为name.type的文件 /name.type
/src/name.type
name 匹配所有名为name的文件、文件夹 /name.txt
/name/file.txt
/src/name.txt
name/ 以"/"为结尾,将匹配所有名为name的文件夹内所有子文件夹与文件 /name/file.txt
/src/pulic/name.txt
/name.type
folder/name.type
"/"在开头或中间(即指定特定文件夹),将以.gitignore所在位置为根目录出发进行匹配 /name.type
/folder/name.type

不匹配:
/src/name.type

**/name
**/folder/name.type
folder/**/name
两个星号将匹配任意数量任意字符(包含分隔符) /src/name/file.txt
/src/test/name/file.txt

/folder/name.file
src/folder/name.file

/folder/src/name/file.txt
/folder/src/test/name.type

*.type
*folder/
单星号将匹配任意数量任意字符(但不包含分隔符) /file.type
/src/file.type

/firstfolder/file.txt

?.type 问号匹配单个字符(但不包含分隔符) /a.type
/b.type
name[a-z].type
name[0-9].type
中括号内声明匹配范围,可为字母,也可为数字 /namea.type
/nameb.type

/namea1.type
/name2.type

name[abc].type 中括号内声明匹配单个字符 /namea.type
/nameb.type
/namec.type
name[!abc].file 与上一个不同,感叹号表示排除 /named.file

不匹配:
/namesa.file

*.file All files withe .file extention /name.file
/lib/name.file
!name.type
!name/file.type
感叹号表示例外,即匹配时排除它们,但如果一个文件的父目录被匹配,是无法排除的

如果书写时感叹号为文件或目录名称内字符,请在感叹号前加一个反斜杠"\"。

 

匹配时属于从上到下顺次匹配,即出现先排除匹配又重新匹配时,将忽略之前的排除。
.gitignore默认所有新配置仅对在保存配置后添加的文件生效,为匹配所有文件,故需要删除暂存区文件后重新将所有文件添加暂存区。

Git实际工作时会查找三个文件$XDG_CONFIG_HOME/git/ignore、$GIT_DIR/info/exclude与.gitignore,$GIT_DIR代表 .git目录位置,$XDG_CONFIG_HOME代表相对于用户git特定的配置文件被存储的目录,Git会从后向前逐个匹配这三个文件的内容。区别是,一般.gitignore会推送,所以是整个项目,所有协作者都会排除的文件,前两个属于用户个人本地需要排除的特定文件,第一个针对全局,即所有本地存储库,第二个仅针对当前存储库。

各类可用模板可查看项目https://github.com/github/gitignore

format占位符

扩展到单个文本字符的占位符

占位符 说明 例子
%n 换行  
%% 一个原始%  
%x00 打印十六进制代码中的一个字节  

影响后面占位符的格式化的占位符

占位符 说明 例子
%Cred 将颜色切换为红色  
%Cgreen 将颜色切换为绿色  
%Cblue 将颜色切换为蓝色  
%Creset 重置颜色  
%C(…)    
%m 左(<)、右(>)或边界(-)标记  
%w([<w>[,<i1>[,<i2>]]])    
%<( <N> [,trunc|ltrunc|mtrunc])    
%<|( <M> )    
%>( <N> ), %>|( <M> )    
%>>( <N> ), %>>|( <M> )    
%><( <N> ), %><|( <M> )    

扩展到从提交中提取的信息的占位符

占位符 说明 例子
%H commit hash  
%h 缩写commit hash  
%T tree hash  
%t 缩写tree hash  
%P parent hashes  
%p 缩写parent hashes  
%an 作者名称  
%aN 作者名称(遵循.mailmap)  
%ae 作者电子邮件  
%aE 作者电子邮件(遵循.mailmap)  
%al 作者电子邮件本地部分(@符号之前的部分)  
%aL 作者电子邮件本地部分(遵循.mailmap)  
%ad 作者日期(格式遵循--date=选项)  
%aD 作者日期,RFC2822样式  
%ar 作者日期,相对  
%at 作者日期,UNIX时间戳  
%ai 作者日期,类似ISO 8601的格式  
%aI 作者日期,严格的ISO 8601格式  
%as 作者日期,短格式(YYYY-MM-DD)  
%ah 作者日期,人类风格  
%cn 提交者名称  
%cN 提交者名称(遵循.mailmap)  
%ce 提交者电子邮件  
%cE 提交者电子邮件(遵循.mailmap)  
%cl 提交者电子邮件本地部分(@符号之前的部分)  
%cL 提交者电子邮件本地部分(遵循.mailmap)  
%cd 提交者日期(格式遵循--date=选项)  
%cD 提交者日期,RFC2822样式  
%cr 提交者日期,相对  
%ct 提交者日期,UNIX时间戳  
%ci 提交者日期,类似ISO 8601的格式  
%cI 提交者日期,严格的ISO 8601格式  
%cs 提交者日期,短格式(YYYY-MM-DD)  
%ch 提交者日期,人类风格  
%d 引用名称,如git-log的--decorate选项  
%D 不带括号"()"的引用名称  
%(describe[:options])    
%S 在命令行中给出的提交名称,通过该名称达到提交的目的  
%e 编码  
%s subject 在提交一节有说明
%f 经过处理主题行,适用于文件名  
%b body  
%B 原始subject和body  
%N commit注释  
%GG 来自GPG的签名提交的原始验证信息  
%G? 签名状态  
%GS 显示已签名的提交的签名者的名称  
%GK 显示用于签署已签名提交的密钥  
%GF 显示用于签署已签名提交的密钥的指纹  
%GP 显示其子密钥用于签署签名提交的主密钥的指纹  
%GT 显示用于签署签名提交的密钥的信任级别  
%gD reflog选择器  
%gd 简化的reflog选择器  
%gn reflog标识名称  
%gN reflog标识名称(遵循.mailmap)  
%ge reflog标识电子邮件  
%gE reflog标识电子邮件(遵循.mailmap)  
%gs reflog subject  
%(trailers[:options])    

评论

  1. Lidocaine
    Windows Edge 110.0.1587.57
    2 年前
    2023-2-28 22:39:24

    13493 words,113 views,0 comment。

  2. who
    Windows Edge 112.0.1722.58
    2 年前
    2023-4-28 13:47:57

    15134 words, 334 views, 1 comment.

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇