本文最后更新于:2024年5月7日 下午

本文介绍git常用命令的使用方法。

Git

译为分布式版本控制系统,是一个开源的分布式版本控制系统,可以有效、高速地处理从很小到非常大的项目版本管理。

安装

  • Linux
1
$ sudo apt install git-all
1
$ sudo dnf install git-all
  • Windows

https://git-scm.com/download/win 下载 gitbash 并安装即可

测试

  • Linux
1
2
3
$ git --version

-> git version 2.7.4 # 出现版本表示安装成功
  • Windows
1
2
3
$ git --version

-> git version 2.25.0.windows.1 # 出现版本表示安装成功

Repository

译为版本库仓库,是git的核心概念,用于存放代码与各个版本的补丁信息和用户配置信息等。

Git 配置

1
2
$ git config --global user.name "your name"
$ git config --global user.email "your_email@youremail.com"

Git 信息流

初次接触git的人需要对git信息流有一个初步的理解,掌握脉络之后就明白各个命令实在干什么了,主要流程如下图:

主要概念

  • workspace(工作区):当前工作目录,它持有实际文件;
  • inedex(暂存区):保存临时改动,workspace的文件改动后通过add命令添加到index;
  • repository(仓库):git管理的本地仓库,index中确定需要的更改提交到仓库中,存放提交的修改与历史变动;
  • HEAD:指向最后一次提交的结果,可以理解为每一次commit提交代码都会在git中产生一个节点,每个节点代表一个代码仓库的历史状态,我们可以在各个节点之间反复横跳,但是一个时刻只能在一个节点上,而标记我们在那个节点的就是HEAD,很像C语言中的指针;
  • Remote(远程端):git的真正妙用在于有统一的服务器管理协同工作的多个开发人员的代码,因此我们经常需要把本地仓库的代码推送(push)到远端,或把远端的代码拉取(fetch/pull)或复制(clone)到本地。

主要命令

仓库管理

git init

Git 使用 git init 命令来初始化一个 Git 仓库,Git 的很多命令都需要在 Git 的仓库中运行,所以 git init 是使用 Git 的第一个命令。

在执行完成 git init 命令后,Git 仓库会生成一个 .git 目录,该目录包含了资源的所有元数据,其他的项目目录保持不变。

1
git init

在现有文件夹下使用该命令,会在当前文件夹创建 .git目录,该目录记录git相关管理信息。

1
git init test_dir

会在当前文件夹下创建test_dir文件夹,并在其中创建.git文件夹。

git clone

git clone 从现有 Git 仓库中拷贝项目。

1
git clone [-b branch] <repo> [directory]

repo: 要克隆的远程仓库地址

branch: 将要克隆的远程仓库分支名称,如果不设置该项参数则会克隆默认分支

directory: 本地仓库文件夹名称,如果不设置该项参数则以仓库名称命名文件夹

  • 协议配置
1
2
3
git clone git@github.com:zywvvd/test.git            --SSH协议
git clone git://github.com/zywvvd/test.git --GIT协议
git clone https://github.com/zywvvd/test.git --HTTPS协议

git clone 时,可以所用不同的协议,包括 ssh, git, https 等,其中最常用的是 ssh,因为速度较快,还可以配置公钥免输入密码。

git config

查看和配置git相关信息。

1
git config --list

查看git配置信息

1
git conifg -e

编辑配置文件(针对当前仓库)

1
git config -e --global

编辑配置文件(针对系统上所有仓库)

1
2
git config --global user.name "vvd"
git config --global user.email vvd@canglan.com

配置提交代码时的用户信息

本地信息流管理

本地信息流主要涉及工作区(workspace)暂存区(index)版本库(repository)

git add

git add 命令可将该文件添加到暂存区。

1
git add [file1] [file2] ...

添加一个或多个文件到暂存区。

1
git add [dir]

添加指定目录到暂存区,包括子目录。

1
git add .

他会监控工作区的状态树,使用它会把工作时的所有变化提交到暂存区,包括文件内容修改(modified)以及新文件(new),但不包括被删除的文件

1
git add -u

git add --update的缩写,他仅监控已经被add的文件(即tracked file),他会将被修改的文件提交到暂存区。add -u 不会提交新文件(untracked file)。

1
git add -A

git add --all的缩写,是上面两个功能的合集。

:个人建议慎用 git add -A,需要明确地知道每步add操作提交了什么,切实管理自己的仓库。

git rm

git rm 命令用于从暂存区和工作区中删除文件(会删除本地文件)。

1
git rm <file>

将文件file从暂存区和工作区中删除。

1
git rm -f <file>

强制删除选项 -f

用于删除之前修改过并且已经放到暂存区域的情况。

1
git rm --cached <file>

--cached 选项,可以把文件从暂存区域移除,但仍然保留在当前工作目录中,即从跟踪清单中删除。

git commit

将暂存区中的内容添加到本地仓库中。

1
git commit -m <message>

将暂存区的内容提交到本地仓库;

此过程中必须附加message信息,提交后会在git中生成唯一的名称记录这一提交,可以在git log命令中查看。

1
git commit [file1] [file2] ... -m <message>

可以对部分文件进行提交。

1
git commit -a

-a 参数设置修改文件后不需要执行 git add 命令,直接来提交。

1
git commit --amend

修改刚刚提交的注释信息

:首次提交必须设置用户信息的名称和邮箱。

git show

显示某次 commit 的修改

1
git commit [commit_id]

git checkout

此命令用来放弃掉所有还没有提交(就是 git add 或 git commit)的修改:内容修改与整个文件删除。但是此命令不会删除掉刚新建的文件。因为刚新建的文件还没已有加入到 git 的管理系统中。

1
git checkout -- <file>

将缓存区的文件覆盖到工作区中,在文件名与分支名没有歧义时可以省略--

如果暂存区有该文件的提交,则从暂存区中抓取文件覆盖当前工作区的文件,否则从最近一次commit中抓取该文件并覆盖当前文件。

总之就是把该文件最近一次 add 或 commit 的副本抓来覆盖了工作区的文件。

1
git checkout .  

这个操作很危险,会清除工作区中未添加到暂存区的改动,放弃所有的工作区文件修改。

1
git checkout HEAD <file>

用 HEAD 指向的 master 分支中的文件替换暂存区和以及工作区中的文件。

1
git checkout HEAD .

用 HEAD 指向的 master 分支中的所有文件替换暂存区和以及工作区中的文件。

注:checkout HEAD是极具危险性的命令,它不但会清除工作区中未提交的改动,也会清除暂存区中未提交的改动。

1
git checkout --orphan <branch name>

将当前所在Head的工作区状态直接带到开辟的全新分支当中,用于清空之前所有提交记录。

切换到新分支时没有任何提交记录,所有现有工作区的文件都是全新的,在添加到暂存区并提交之后才有了第一次提交。

git reset

git reset 命令用于回退版本,可以指定退回某一次提交的版本。

1
git reset [--soft | --mixed | --hard] [HEAD] [file]
  • --mixed 为默认,可以不用带该参数,用于重置暂存区的文件与上一次的提交(commit)保持一致,工作区文件内容保持不变。

    1
    git reset HEAD  # 取消之前 git add 添加过的缓存。

    暂存区的目录树会被重写,被 master 分支指向的目录树所替换,但是工作区不受影响。

    实例:

    1
    2
    3
    $ git reset HEAD^            # 回退所有内容到上一个版本  
    $ git reset HEAD^ hello.php # 回退 hello.php 文件的版本到上一个版本
    $ git reset 052e # 回退到指定版本
  • --soft 参数用于回退到某个版本:

    实例:

    1
    git reset --soft HEAD~3  #  回退上上上一个版本
  • --hard 参数撤销工作区中所有未提交的修改内容,将暂存区与工作区都回到上一次版本,并删除之前的所有信息提交:

    1
    git reset --hard HEAD

    实例:

    1
    2
    3
    $ git reset –hard HEAD~3  # 回退上上上一个版本  
    $ git reset –hard bae128 # 回退到某个版本回退点之前的所有信息。
    $ git reset --hard origin/master # 将本地的状态回退到和远程的一样

    **注:**谨慎使用 –hard 参数,它会删除回退点之前的所有信息。

  • HEAD 说明

    • HEAD 表示当前版本
    • HEAD^ 上一个版本
    • HEAD^^ 上上一个版本
    • HEAD^^^ 上上上一个版本
    • 以此类推…

    可以使用 ~数字表示

    • HEAD~0 表示当前版本
    • HEAD~1 上一个版本
    • HEAD^2 上上一个版本
    • HEAD^3 上上上一个版本
    • 以此类推…

git diff

比较文件在暂存区和工作区的差异,即显示已写入暂存区和已经被修改但尚未写入暂存区文件对区别。

1
git diff [file]

显示暂存区和工作区的差异。

1
2
3
git diff --cached [file]

git diff --staged [file]

显示暂存区和上一次提交(commit)的差异。

1
git diff [first-branch]...[second-branch]

显示两次提交之间的差异。

git mv

用于移动或重命名一个文件、目录或软连接。

1
git mv [file] [newfile] [-f]

在git中移动文件

-f: 强制文件移动(如果已经存在该文件则会覆盖)

git status

查看仓库当前的状态,显示有变更的文件,用于查看在你上次提交之后是否有对文件进行再次修改。

1
git status

详细显示工作区和暂存区的修改、提交状态。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
On branch master
On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
new file: B
new file: C
new file: D

Changes not staged for commit:
(use "git add/rm <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
deleted: C
modified: D
modified: E

Changes to be committed:
(use "git restore --staged <file>..." to unstage)
renamed: D -> F

Untracked files:
(use "git add <file>..." to include in what will be committed)
A
1
git status -s

简略输出状态结果。

1
2
3
4
5
6
7
A  B
AD C
AM D
M E
D a.txt
R D -> F
?? A
  • 符号说明
    • A(Added): 新添加到暂存区的文件
    • M(Modified): 已经提交过并被修改的文件
    • D(Deleted): 已经提交过并被直接删除的文件
    • R(Renamed): 重命名的文件
    • AD: 已经提交到暂存区后被删除
    • AM: 已经提交到暂存区后被修改
    • ??(Untracked): 未追踪的文件

git stash

1
git stash

将所有未提交的修改(工作区和暂存区)保存至堆栈中,用于后续恢复当前工作目录。

1
git stash save

作用等同于git stash,区别是可以加一些注释:

例如:

1
2
3
4
5
6
7
git stash的效果:

stash@{0}: WIP on master: b2f489c second
1
git stash save “test1”的效果:

stash@{0}: On master: test1
1
git stash list

查看当前stash中的内容

1
git stash pop

将当前stash中的内容弹出,并应用到当前分支对应的工作目录上。
注:该命令将堆栈中最近保存的内容删除(栈是先进后出)

1
git stash apply

将堆栈中的内容应用到当前目录,不同于git stash pop,该命令不会将内容从堆栈中删除,也就说该命令能够将堆栈的内容多次应用到工作目录中,适应于多个分支的情况。

可以使用git stash apply + stash名字(如stash@{1})指定恢复哪个stash到当前的工作目录。

1
git stash drop + 名字

从堆栈中移除某个指定的stash

1
git stash clear

清除堆栈中的所有 内容

1
git stash show

查看堆栈中最新保存的stash和当前目录的差异。

例如:

1
2
3
$ git stash show
src/main/java/com/wy/StringTest.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

git stash show stash@{1}查看指定的stash和当前目录差异。

通过 git stash show -p 查看详细的不同:

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
$ git stash show -p
diff --git a/src/main/java/com/wy/CacheTest.java b/src/main/java/com/wy/CacheTest.java
index 6e90837..de0e47b 100644
--- a/src/main/java/com/wy/CacheTest.java
+++ b/src/main/java/com/wy/CacheTest.java
@@ -7,6 +7,6 @@ package com.wy;
*/
public class CacheTest {
public static void main(String[] args) {
- System.out.println("git stash test");
+ System.out.println("git stash test1");
}
}
diff --git a/src/main/java/com/wy/StringTest.java b/src/main/java/com/wy/StringTest.java
index a7e146c..711d63f 100644
--- a/src/main/java/com/wy/StringTest.java
+++ b/src/main/java/com/wy/StringTest.java
@@ -12,7 +12,7 @@ public class StringTest {

@Test
public void test1() {
- System.out.println("=================");
+ System.out.println("git stash test1");
System.out.println(Strings.isNullOrEmpty(""));//true
System.out.println(Strings.isNullOrEmpty(" "));//false
System.out.println(Strings.nullToEmpty(null));//""
1
git stash branch

从最新的stash创建分支。

git revert

通过创建一次新的 commit 来撤销一次 commit 所做出的修改。这种撤销的方式是安全的,因为它并不修改 commitm history

1
git revert HEAD~2

将会查出倒数第二次(即当前commit的往前一次)提交的修改,并创建一个新的提交,用于撤销当前提交的上一次 commit

历史记录管理

git log

查看历史提交记录。

1
git log [--oneline]

显示提交记录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ git log
commit 5ac3ea4f50f0f558d8ba36ef7b713054c4cfee69 (HEAD -> master)
Author: vvd-from-xiaobing's-notebook <zywvvd@mail.ustc.edu.cn>
Date: Sat Aug 29 16:11:56 2020 +0800

save

commit 50f554063e4ca3502dfc318817522aee88863d62
Author: vvd-from-xiaobing's-notebook <zywvvd@mail.ustc.edu.cn>
Date: Sat Aug 29 15:56:13 2020 +0800

add-e

commit 3de3b277020322cd8f6991987fe38158d3fcdfea
Author: vvd-from-xiaobing's-notebook <zywvvd@mail.ustc.edu.cn>
Date: Sat Aug 29 15:36:23 2020 +0800

save
1
git log [--oneline]

--oneline: 简略显示提交记录日志

1
2
3
4
5
$ git log --oneline
5ac3ea4 (HEAD -> master) save
50f5540 add-e
3de3b27 save
9313997 save
1
git log [--graph] [--reverse]

--graph: 查看历史中什么时候出现了分支、合并;

--reverse: 正常上方显示最新的提交信息,该参数可以反向输出信息;

1
git log [--author] [--since] [--before] [--until] [--after]

--author: 只想查找指定用户的提交日志;

--since --until --until --after: 按照时间过滤信息

实例:

1
git log --oneline --before={3.weeks.ago} --after={2010-04-18} --no-merges

git blame

git blame用来追溯一个指定文件的历史修改记录。它能显示任何文件中每行最后一次修改的提交记录。 所以,如果你在代码中看到有一个bug,你可以使用 git blame 标注这个文件,查看哪一次提交引入了这行。

1
git blame <file>

查看 file文件的修改记录。

可以使用 -L 指定文件的行数范围:

1
git blame -L n1,n2 filename

远程信息流管理

git remote

用于在远程仓库的操作。

1
git remote -v

显示所有远程仓库。

实例:

1
2
3
4
5
$ git clone git@github.com:zywvvd/Python_Practise.git
$ cd Python_Practise
$ git remote -v
origin git@github.com:zywvvd/Python_Practise.git (fetch)
origin git@github.com:zywvvd/Python_Practise.git (push)

origin 为远程地址的别名。

1
git remote show [remote]

显示某个远程仓库的信息。

实例:

1
2
3
4
5
6
7
$ git remote show git@github.com:zywvvd/Python_Practise.git
* remote git@github.com:zywvvd/Python_Practise.git
Fetch URL: git@github.com:zywvvd/Python_Practise.git
Push URL: git@github.com:zywvvd/Python_Practise.git
HEAD branch: master
Local ref configured for 'git push':
master pushes to master (up to date)
1
git remote add [shortname] [url]

添加远程版本库。

实例:

1
2
3
# 提交到 Github
$ git remote add origin git@github.com:zywvvd/Python_Practise.git
$ git push -u origin master
1
git remote rm name

删除远程仓库。

1
git remote rename old_name new_name

修改仓库名。

git fetch

用于从远程获取代码库。

1
git fetch <remote>

获取远程remote仓库的信息,取回到本地。

1
git fetch <remote> <branch> 

取回特定分支的更新,可以指定分支名。

实例:

1
git fetch origin master
1
git fetch --all

拉取所有可见分支

git pull

从一个仓库或者本地的分支拉取并且整合代码。

1
git pull [<options>] [<repository> [<refspec>]]

git pull相当于 git fetch 跟着一个 git merge FETCH_HEAD<repository>是仓库的名字,<refspec> 是分支的名字。如果都不写,会有一个默认值。

实例:

1
git pull origin master

拉取远程服务器originmaster分支。

1
git pull --all

拉取远程所有可见分支

注:git pull = git fetch + git merge。

git push

用于将本地分支的更新,推送到远程主机。

1
git push <远程主机名> <本地分支名>:<远程分支名>

分支推送顺序的写法是<来源地>:<目的地>,所以git pull是<远程分支>:<本地分支>,而git push是<本地分支>:<远程分支>。

1
git push origin master:master

将本地的master分支推送到origin主机的master分支,如果后者不存在,则会被新建。

1
git push origin master

如果省略远程分支名,则表示将本地分支推送与之存在”追踪关系”的远程分支(通常两者同名),如果该远程分支不存在,则会被新建。

1
git push origin

如果当前分支与远程分支之间存在追踪关系,则本地分支和远程分支都可以省略。

1
git push

如果当前分支只有一个追踪分支,那么主机名都可以省略。

注:不带任何参数的git push,默认只推送当前分支,这叫做simple方式。此外,还有一种matching方式,会推送所有有对应的远程分支的本地分支。Git 2.0版本之前,默认采用matching方法,现在改为默认采用simple方式。如果要修改这个设置,可以采用git config命令。

1
2
3
git config --global push.default matching

git config --global push.default simple
1
git push -u origin master

如果当前分支与多个主机存在追踪关系,则可以使用-u选项指定一个默认主机,这样后面就可以不加任何参数使用git push。

1
git push --all origin

将所有本地分支都推送到origin主机。

1
2
3
git push origin :master

git push origin --delete master

如果省略本地分支名,则表示删除指定的远程分支,因为这等同于推送一个空的本地分支到远程分支。

1
git push --force origin

如果远程主机的版本比本地版本更新,推送时Git会报错,要求先在本地做git pull合并差异,然后再推送到远程主机。这时,如果你一定要推送,可以使用–force选项。

上面命令使用–force选项,结果导致在远程主机产生一个”非直进式”的合并(non-fast-forward merge)。除非你很确定要这样做,否则应该尽量避免使用–force选项。

分支管理

git branch

用于分支管理。

1
git branch

列出本地分支。

1
git branch -v 

查看每一个分支的最后一次提交。

1
--merged` 与 `--no-merged` 这两个有用的选项可以过滤这个列表中已经合并或尚未合并到当前分支的分支。 如果要查看哪些分支已经合并到当前分支,可以运行 `git branch --merged
1
2
3
git branch --remote

git branch -r

列出远程分支。

1
2
3
git branch --all

git branch -a

列出本地和远程所有分支。

1
git branch <branchname>

创建分支。

1
git branch -d <branchname>

删除本地分支。

1
git branch -d -r <branchname>

删除远程分支,删除后还需推送到服务器 git push origin:<branchname>

1
git branch -m <oldbranch> <newbranch>

重命名本地分支。

git checkout

checkout 命令除了放弃掉所有还没有提交的修改外,还有切换分支的功能。

1
git checkout <branchname>

切换到指定分支。

1
git checkout -b <branchname>

创建分支并切换过去。

git merge

用于从指定的commit(s)合并到当前分支的操作,有以下两种用途:

  1. 用于git-pull中,来整合另一代码仓库中的变化(即:git pull = git fetch + git merge)
  2. 用于从一个分支到另一个分支的合并
1
git merge -m <msg> <commit>

commit合并到当前分支。

1
git merge --abort

该命令仅仅在合并后导致冲突时才使用。git merge --abort将会抛弃合并过程并且尝试重建合并前的状态。但是,当合并开始时如果存在未commit的文件,git merge --abort在某些情况下将无法重现合并前的状态(特别是这些未commit的文件在合并的过程中将会被修改时)。

1
2
3
git merge --commit

git merge --no-commit

--commit参数使得合并后产生一个合并结果的commit节点。该参数可以覆盖--no-commit
--no-commit参数使得合并后,为了防止合并失败并不自动提交,能够给使用者一个机会在提交前审视和修改合并结果。

1
2
3
git merge --edit / -e

git merge --no-edit

--edit-e用于在成功合并、提交前调用编辑器来进一步编辑自动生成的合并信息。因此使用者能够进一步解释和判断合并的结果。
--no-edit参数能够用于接受自动合并的信息(通常情况下并不鼓励这样做)。

1
git merge --ff

--ff是指fast-forward命令。当使用fast-forward模式进行合并时,将不会创造一个新的commit节点。默认情况下,git-merge采用fast-forward模式。

1
git merge --ff-only

除非当前HEAD节点已经up-to-date(更新指向到最新节点)或者能够使用fast-forward模式进行合并,否则的话将拒绝合并,并返回一个失败状态。

1
2
3
git merge --log[=<n>]

git merge --no-log

--log[=<n>]将在合并提交时,除了含有分支名以外,还将含有最多n个被合并commit节点的日志信息。
--no-log并不会列出该信息。

1
2
3
git merge --stat

git merge -n / --no-stat

--stat参数将会在合并结果的末端显示文件差异的状态。文件差异的状态也可以在git配置文件中的merge.stat配置。
相反,-n, --no-stat参数将不会显示该信息。

1
2
3
git merge --squash  

git merge --no-squash

--squash 当一个合并发生时,从当前分支和对方分支的共同祖先节点之后的对方分支节点,一直到对方分支的顶部节点将会压缩在一起,使用者可以经过审视后进行提交,产生一个新的节点。

注: 该参数和--no-ff冲突

1
2
3
git merge -q / --quiet

git merge -v / --verbose

-q / --quiet : 静默操作,不显示合并进度信息;

-v / --verbose: 显示详细的合并结果信息。

git rebase

1
git rebase -i 

将本地的多次提交合并为一个,以简化提交历史。本地有多个提交时,如果不进行这一步,在git rebase master时会多次解决冲突(最坏情况下,每一个提交都会相应解决一个冲突)

示例:

1
2
3
4
5
6
7
8
git checkout master
git pull
git checkout local
git rebase -i HEAD~2 //合并提交 --- 2表示合并两个
git rebase master---->解决冲突--->git rebase --continue
git checkout master
git merge local
git push

标签

git tag

给仓库历史中的某一个提交打上标签

1
git tag <name>

给当前commit打上轻量标签

1
2
3
4
5
git log --oneline
49a021d (HEAD -> master) c
cf68aa3 d
5318a62 (tag: tag-test, dev) b added:
75c5c3e add-a
1
git tag -l <re>

列出仓库中的tag列表

可以使用正则表达式选择感兴趣的标签

1
2
git tag -l "test*"
test-2
1
git tag -a <name> -m <comments>

创建附注标签,通过使用 git show 命令可以看到标签信息和与之对应的提交信息:

1
2
3
4
5
6
7
8
9
10
$ git show test-2
commit 49a021da8afad6b33f41a21fce057040c8ae8f80 (HEAD -> master, tag: test-2)
Author: zywvvd <zywvvd@mail.ustc.edu.cn>
Date: Sat Oct 17 14:22:13 2020 +0800

c

diff --git a/c b/c
new file mode 100644
index 0000000..e69de29

也可以为某个commit打标签:

1
git tag -a v1.2 9fceb02
1
git push origin <tagname>

推送标签,普通的git push并不会把本地的标签推送到远程,需要手动推送标签。

1
git push origin --tags

如果想要一次性推送很多标签,也可以使用带有 --tags 选项的 git push 命令。 这将会把所有不在远程仓库服务器上的标签全部传送到那里。

1
git tag -d <tagname>

删除掉你本地仓库上的标签。

1
git push origin :refs/tags/<tagname>

删除远程标签

1
git checkout 2.0.0

检出标签所在的commit,会处于“分离头指针(detached HEAD)”的状态。

git 选项

  • -d --delete:删除
  • -f --force:强制
  • -m --move:移动或重命名
  • -r --remote:远程
  • -a --all:所有

参考资料:



文章链接:
https://www.zywvvd.com/notes/tools/git/git-usage/git-usage/


“觉得不错的话,给点打赏吧 ୧(๑•̀⌄•́๑)૭”

微信二维码

微信支付

支付宝二维码

支付宝支付

Git - 常用命令使用教程
https://www.zywvvd.com/notes/tools/git/git-usage/git-usage/
作者
Yiwei Zhang
发布于
2020年5月6日
许可协议