写点什么

git 使用总结

作者:攻城狮MK
  • 2022 年 1 月 28 日
  • 本文字数:9399 字

    阅读完需:约 31 分钟

git 使用总结

前言

文章结合工作场景总结了常用的 git 命令的在本地使用以及使用过程中的注意事项,特别是一些误操作如何撤销,以及同样能达到目的或者感觉效果一样的命令的区别,同时也推荐了一些使用方式,比如提交模板、log 定制、借助 git 得到一些统计信息。写这篇总结的时候拓展了自己对 git 的认知,同时也希望对你有帮助。写完这篇总结后,发现自己还有很多不知道的,也在继续了解实践中,所以如果你有相关的问题,欢迎评论区留言,在一段时间内,我也在继续关注着这些问题。


说下内容排版

先讲 git log,因为在介绍其他命令操作的时候,经常会使用 git log 查看提交历史,所以会先把 git log 的使用介绍一遍,这样在继续看的同时,大家就已经把 git log 实践了一遍了。

然后就是按照正常的顺序一次讲解工作中用到的几个命令

git log

当我们在项目中使用 git log 命令时,可以看到下面的输出:


$ git logcommit de6c6d0f98f67a501edcf36219ed0eee9a726578 (HEAD -> main)Author: MK <1561207310@qq.com>Date:   Thu Jan 13 21:51:23 2022 +0800
first commit
commit 175e2637779c544d2b141ac18b3eb5f9856ea969 (origin/main, origin/HEAD)Author: MK <1561207310@qq.com>Date: Thu Jan 13 19:55:02 2022 +0800
Initial commit
复制代码


但一般不会这样单纯的使用 git log 命令,因为在 idea 中,使用快捷键 <cmd + 9> 有更好的视觉效果:



如果只看上一次的 log 日志,我们可以使用 git log -1 HEAD


当我想详细的查看最近的几次提交的时候,我会使用 idea 的这个 git log 面板工具,它可以在左侧窗口灵活的切换不同的分支来查看分支上最近的提交历史,同时针对不同的历史提交,可以在右侧窗口查看详细的更新文件和提交信息。


但是,当我浏览当前项目近期的提交历史记录的时候时候,idea 的这个工具面板又显得不是那么方便了,因为他没有 git log 命令本身提供的一些参数控制的那么全面,下面我们会一起来看几个例子,为了目录方便,这里我们分下模块,按照选项过滤和输出样式两部分来说明。

过滤选项

过滤选项的目的是为了过滤出那些我们感兴趣的提交历史


1.限制输出提交历史记录数量,比如只想看最近 10 次提交历史


$ git log -10
复制代码


2.限制某个日期往后的提交历史,比如只想看 2022-1-1 日之后的提交历史。当然也可以看某个时间之前的提交历史,使用--before='<日期>',两者结合使用也 OK,甚至可以使用相对时间,当我们一般不会用


$ git log --after="2022-1-1"
复制代码


3.限制查看某个作者的提交历史,比如只想看 maike 的提交历史


$ git log --author='maike'# 总有人会想同时查看两个人的提交历史,比如 maike 和 tom 的$ git log --author='maike\|tom'
复制代码


4.根据提交信息进行过滤


$ git log --grep='first commit'
复制代码


5.根据修改的文件进行过滤,比如只查看更新了 README.md 文件的提交历史


$ git log -- README.md
复制代码


6.根据修改内容进行过滤,比如想知道那行神奇代码 Hello world! 是哪个提交


$ git log -S'Hello world!'
复制代码


7.查看从主分支 main 分离到分支 branch-a 后的所有提交历史


$ git log main..branch-a# 这里我们需要做个对比,方便理解# 首先切到 main 分支,看下提交历史$ git checkout main && git log --onelinef6fe849 (HEAD -> main) 将 .idea 加入 .gitignore 文件181914a first commit175e263 (origin/main, origin/HEAD) Initial commit
# 可以看到 main 分支有三个提交历史# 再切到 branch-a 分支查看提交历史$ git checkout branch-a && git log --oneline114aba5 (HEAD -> branch-a) add a.txtf6fe849 (main) 将 .idea 加入 .gitignore 文件181914a first commit175e263 (origin/main, origin/HEAD) Initial commit
# 可以看到,branch-a 的提交历史中多了一个 114aba5# 我们执行 git log main..branch-a 看看$ git log main..branch-a114aba5 (HEAD -> branch-a) add a.txt
# 可以看到这时只出现了一个 114aba5 提交记录
复制代码


8.不想看合并分支的提交记录,在执行分支合并的时候,会生成一个 commit,这个 commit 如果我们不关心,可以过滤掉


$ git log --no-merges
# 或者我们只想看 merge 记录$ git log --merges
复制代码

输出格式

其实刚刚已经用到了输出格式相关的选项 --oneline,它可以在我们查看简要提交信息的时候使用,包含 commit id, commit message,references,并展示在一行,reference 是指提交历史所关联的 branch 或者 tag 等。


我们接着来看其他几个跟输出格式相关的选项


1.graph,该选项使用 ASCII 字符串来形象地展示你的分支、合并历史


$ git log --graph --oneline*   453592a (HEAD -> main) Merge branch 'branch-a'|\| * c162590 (branch-a) add another line to a.txt* | 2df282d add main.txt* | 7ecbc7a (branch-b) add b.txt|/* 114aba5 add a.txt* f6fe849 将 .idea 加入 .gitignore 文件* 181914a first commit* 175e263 (origin/main, origin/HEAD) Initial commit
复制代码


2.stat,该选项会显示每次提交的文件修改统计信息


$ git long --graph --oneline --stat*   453592a (HEAD -> main) Merge branch 'branch-a'|\| * c162590 (branch-a) add another line to a.txt| |  a.txt | 1 +| |  1 file changed, 1 insertion(+)* | 2df282d add main.txt| |  main.txt | 1 +| |  1 file changed, 1 insertion(+)* | 7ecbc7a (branch-b) add b.txt|/|    b.txt | 1 +|    1 file changed, 1 insertion(+)* 114aba5 add a.txt|  a.txt | 1 +|  1 file changed, 1 insertion(+)* f6fe849 将 .idea 加入 .gitignore 文件|  .gitignore | 1 +|  1 file changed, 1 insertion(+)* 181914a first commit|  README.md | 51 +++++++++++++++++++++++++++++++++++++++++++++++++--|  1 file changed, 49 insertions(+), 2 deletions(-)* 175e263 (origin/main, origin/HEAD) Initial commit   .gitignore |  23 ++++++++++++++   LICENSE    | 200 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++   README.md  |   2 ++   3 files changed, 226 insertions(+)
复制代码


3.abbrev-commit,仅显示 SHA-1 校验和所有 40 个字符中的前几个字符。


# 举个例子$ git log -1commit 453592a65be3f33c9bcdedee2c117e8415d67be0 (HEAD -> main)Merge: 2df282d c162590Author: maike <1561207310@qq.com>Date:   Thu Jan 13 23:35:20 2022 +0800
Merge branch 'branch-a' # 看效果$ git log --abbrev-commit -1commit 453592a (HEAD -> main)Merge: 2df282d c162590Author: maike <1561207310@qq.com>Date: Thu Jan 13 23:35:20 2022 +0800
Merge branch 'branch-a'
复制代码


4.date,日期格式化


$ git log --date=format:'%Y-%m-%d %H:%M:%S' -1commit 453592a65be3f33c9bcdedee2c117e8415d67be0 (HEAD -> main)Merge: 2df282d c162590Author: maike <1561207310@qq.com>Date:   2022-01-13 23:35:20
Merge branch 'branch-a'
复制代码


5.pretty,使用其他格式显示历史提交信息。可用的选项包括 oneline、short、full、fuller 和 format,我们还可以加上颜色区分


$ git log --pretty=format:'%Cred%h%Creset - %C(yellow)%d%Cblue %s %Cgreen(%cd) %C(bold blue)<%an>%Creset'
复制代码


这里只能截图看效果了:



好了,到这里定制输出样式和过滤筛选都说完了,接下来就靠大家的实践了。


这里给出我最常用的两条 git log 配置(网上找的好的),大家可以参考:


$ git config --global alias.lf "log --no-merges --color --graph --date=format:'%Y-%m-%d %H:%M:%S' --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Cblue %s %Cgreen(%cd) %C(bold blue)<%an>%Creset' --abbrev-commit"
$ git config --global alias.lfs "log --no-merges --color --stat --graph --date=format:'%Y-%m-%d %H:%M:%S' --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Cblue %s %Cgreen(%cd) %C(bold blue)<%an>%Creset' --abbrev-commit"
复制代码


好了,到这里为止,git log 已经说完了,接下来会按照我们工作中 git 使用顺序来安排文章的顺序,首先是 git add 命令

git add

将工作区改动内容或新增内容加入暂存区。


说明:喜欢思考的同学可能会问为什么需要暂存区?为什么不直接提交呢?

我们写代码的时候,并不会主观的按照逻辑分好改动范围后进行,而是连贯高效专注的完成一系列工作,比如在编写新需求的同时顺手修改一下别人的 bug,如果没有暂存区,我们就需要把手头的工作全部作为一次提交,而我们倡导的原子提交是需要按逻辑分开进行的,这样查看提交历史的时候也更清晰,一目了然,更重要的是方便跟踪 bug 和恢复变更。暂存区可以让我们按逻辑选择需要提交的内容放入暂存区,然后进行提交,将工作内容多次进行暂存提交操作,形成一个好的提交记录。这就解决了既能连贯高效工作又能保证原子性提交了。

常用命令

# 添加文件$ git add <file># 添加目录# 'git add .' 这个 . 是指当前文件夹,应该不用说的,废话了$ git add <directory>
复制代码


不仅可以针对文件夹和文件进行暂存,git 还可以针对某个文件中的某几行代码进行暂存,使用 git add -i 命令。很少用,这里不详细说了,知道有这么回事可以了

撤销操作

撤销工作区的改动内容

# 撤销工作区的文件改动$ git checkout -- <file>
复制代码

撤销暂存区的改动内容

# 将暂存区中的文件变更撤销$ git reset <file>
复制代码


git stash

这个命令叫贮藏,我之前没用过,在写这篇 git 总结的时候我发现了它,仿佛打开了新世界的大门,因为我一直有个痛点让我很苦恼,这个命令就是解决方案。


比如我本来在 feature 分支上写着产品丢过来的需求,然后代码写的正酣,来了个线上 bug,我脑子里千头万绪还没缕好,代码已经写了很多了,这个时候分开提交肯定很费神费时间,但又不能直接执行一下 git add ; git commit,(之前我就是这么干的),然后切换到 bugfix 分支去改 bug。


这个时候我们可以使用 git stash 命令将当前工作内容临时存起来,等我们改完 bug 后再取出来 git stash apply


very perfect!


这个命令,目前简单的存取就够我用一段时间了,暂不深入研究。有高手的评论区告诉我一声

git commit

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


git commit 会将当前项目作为一个快照存起来并生成一个提交对象。

而分支则是一个指向 commit 的指针。

如何控制多个分支的前进走向呢?通过 HEAD

常用命令

# 将暂存区的文件提交,并指定 commit message$ git commit -am "commit message" 
复制代码


直接使用 git commit 会打开一个文本编辑器,默认是一个 vi 编辑器,让你输入提交信息,默认是这样的


提交模板

我们也可以配置提交模板,我在网上找了一份 git commit 模板 git-commit-template.txt


设置一下:


# 全局设置提交模板,path 是 git-commit-template.txt 的路径$ git config --global commit.template path/git-commit-template.txt
复制代码


再次执行 git commit,效果如图



如果使用 idea 的话,idea 上的提交模板插件 Git Commit Template,效果很好,建议使用,篇幅关系就不深入介绍了。


好的提交可以产生好的提交记录,使用 git log 查看的时候更赏心悦目,最关键的是,好的提交历史更显专业。

排除文件

.gitignore 文件


一般我们总会有些文件无需纳入 Git 的管理,也不希望它们总出现在未跟踪文件列表。 通常都是些自动生成的文件,比如日志文件,或者编译过程中创建的临时文件等。 在这种情况下,我们可以创建一个名为 .gitignore 的文件,列出要忽略的文件的模式。


.gitignore 的格式规范如下:


  • 所有空行或者以 # 开头的行都会被 Git 忽略。

  • 可以使用标准的 glob 模式匹配,它会递归地应用在整个工作区中。

  • 匹配模式可以以(/)开头防止递归。

  • 匹配模式可以以(/)结尾指定目录。

  • 要忽略指定模式以外的文件或目录,可以在模式前加上叹号(!)取反。


我们看个 .gitignore 文件的例子:


# 忽略所有的 .a 文件*.a
# 但跟踪所有的 lib.a, 即便你在前面忽略了 .a 文件!lib.a
# 只忽略当前目录下的 TODO 文件,而不忽略 subdir/TODO/TODO
# 忽略任何目录下名为 build 的文件夹build/
# 忽略 doc/notes.txt,但不忽略 doc/server/arch.txtdoc/*.txt
# 忽略 doc/ 目录及其所有子目录下的 .pdf 文件doc/**/*.pdf
复制代码


git config core.excludesfile


前面说的 .gitignore 文件用于排除项目中无需跟踪的文件,当我们需要在所有的版本库中忽略某一类文件时,就可以通过该配置项设置全局生效的 .gitignore 文件 。


例如 MacOS 系统的 .DS_Store,


在用户主目录创建一个 .gitignore_global 文件:~/.gitignore_global


# 排除 .Ds_Store 文件.DS_Store
复制代码


设置全局生效


$ git config --global core.excludesfile ~/gitignore_global
复制代码

撤销操作

修改提交信息

$ git commit --amend
复制代码


执行这条命令,将会弹出一个编辑器,编辑器中带入了上个提交的提交信息,更新完关闭后会生成一个新的提交,在 git log 中已经看不到之前的那个提交记录了。

修改提交内容

如果只是修改提交内容,不需要更新提交信息,则只需要将补充的提交内容添加到暂存区,然后执行以下命令


# 同时需要补充提交信息,不带 --no-edit 选项即可$ git commit --amend --no-edit
复制代码

git diff

git diff 命令用于比较工作区、暂存区、本地仓库、远程仓库以及不同分支之间的差异

比较工作区与暂存区

直接使用 git diff 比较的是工作区与暂存区的差异


我们更改一下 a.txt 文件,注意 a.txt 已经被 track 了,更新之后使用 git diff 可以查看到我们新增了 "aaaaaaa"。


c.txt 为什么没有?


比较暂存区与版本库

使用 git diff --cached <commit_id>,用于比较暂存区与版本库的差异,commit_id 可以省略,默认比较的是暂存区与当前版本库的差异


还是上一个例子,我们把 c.txt 加入暂存区,然后执行 git diff --cached 命令


比较工作区与版本库

使用 git diff <commit-id> ,查看工作区与版本库的差异,一般常用 git diff HEAD 查看当前工作区与最新提交做比较,还是上一个例子,我们执行一下 git diff HEAD 看下结果:


比较两个分支

使用 git diff branch-a branch-b 查看 branch-a 分支与 branch-b 分支的差异


还是上一个例子,我们先查看下 main 分支与 branch-b 分支的差异



main 分支包含 a.txt、c.txt、file_1k、LICENSE、README.md 文件,其中 c.txt 是新增的,a.txt 有改动。


使用 git stash 命令将这两个改动临时存起来,切换到 branch-b 分支。可以看到 branch_b 比 main 分支多了一个 b.txt 文件。


我们执行一些 git diff main branch_b,查看结果:



右边窗口把贮藏区的内容取出来再做一次 diff,为什么这里要做个对比呢?其实 branch 是一个指向某个提交的指针,随着提交进行移动,虽然我们有新增 c.txt 文件,也改了 a.txt 文件,但我们并没有提交,所以 main 分支对应的 commit 与 branch_b 对应的 commit 进行 diff 时不会受这两个文件的影响。

merge vs rebase

整合不同分支的修改有两种方法,最常用的是 merge,就是常说的合并代码,还有一种是 rebase,叫变基。不知道 rebase 也没什么关系,用 merge 是没错的,而用 rebase 可能会让同事认为你在搞事情。

merge

快速合并和三路合并

fast forward


当我们从 master 分支检出 branch_b 分支后,master 分支没有更新,一致停留在 C2 这个提交点,branch_b 前进至 C4 这个提交点,这个时候我们在 master 分支上执行 git merge branch_b 命令,master 分支指针直接前进到 C4 这个提交点(如图一所示)。整个提交历史看起来是线性的(如图二所示)



图一



图二


three way


在上述场景中,如果 master 在合并 branch_b 之前进行了一次提交到 C5 这个提交点,这个时候将位于 C4 提交点的 branch_b 合并到 master,将不会进行快速合并,而是新生成一个提交 C6,C6 将有两个父提交(如图三)。同时形成一个合并交叉的提交历史(如图四)



图三


为了找回刚刚那种情况,我们需要先将 main 分支的合并操作撤销,这里使用 git reset 命令

$ git reset --hard 9d4bbdc



图四

rebase

在 merge 的三路合并中,会将 main 分支的 C5 快照与 branch_b 的 C4 快照以及两个分支的共同祖先 C2 进行三方合并,合并生成一个新的快照并提交形成 C6。


rebase 也可以达到同样的效果,但 rebase 的处理过程不一样,比如我们执行以下命令:


# 切换到 branch_b 分支$ git checkout branch_b# 变基到 main 分支$ git rebase main
复制代码



# 切换到 main 分支$ git checkout main# 再合并 branch_b 分支$ git merge branch_b
复制代码



我们用 git log --oneline --graph 看下提交历史:



对比下之前 merge 的三路合并,rebase 形成的提交历史是线性的,且没有形成新的提交。在 branch_b rebase main 的过程中,只是将 branch_b 分支的 C3 和 C4 在 C5 上进行重放。


问题 1:那我们用 merge 还是 rebase 呢?

如果没有要求使用 rebase,那就使用 merge

问题 2:什么情况下使用 rebase

当你被要求需要确保在向远程分支推送时能保持提交历史的整洁的时候

reset vs revert

在演示 merge 和 rebase 的时候,为了重复使用 main 分支合 branch_b 分支形成对比,我使用了 reset 重置 main 分支。


如果用的不多,还有一个命令 revert 经常会与 reset 搞混,现在我们用例子来操作一下,让印象更深刻


经过上面的操作,现在 main 分支已经形成的如下的提交历史


* 3a9e49f (HEAD -> main, branch_b) 更新 b.txt* 878244b (origin/branch_b) add b.txt* 9d4bbdc (origin/main) first commit(END)
复制代码


现在我们分别使用 reset 和 revert 进行一下操作

RESET

使用 reset 命令,git 会把要回退版本之后提交的修改都删除掉


执行git reset --hard 3a52650


* 9d4bbdc (HEAD -> main, origin/main) first commit(END)
复制代码


可以看到 master 当前指向 9d4bbdc 这个版本,我们回到了 main 分支最初的提交。


如果 reset 的时候误操作了咋办?如何撤销 reset 呢?


比如我们刚刚把准备演示的三个提交重置到初次提交了,现在要恢复回来。


可以再使用 git reset --hard 3a9e49f,重置到之前的状态,当然因为我在上面记录了 reset 之前的提交历史,知道之前的提交点是 3a9e49f,一般是不会记得,但是没关系,有一个 git reflog 命令可以查看本地的 HEAD 历史。


我们现在执行一下这个命令,看下结果:


REVERT

经过恢复,我们现在的提交历史还是之前的样子:


* 3a9e49f (HEAD -> main, branch_b) 更新 b.txt* 878244b (origin/branch_b) add b.txt* 9d4bbdc (origin/main) first commit(END)
复制代码


在这基础上,我们继续提交


# 1a7bdea 新增 c.txt 并写入 ”1111111“$ echo "1111111" > c.txt$ git add c.txt; git commit -m "add b.txt"# d46cf7d 追加写入”2222222“$ echo "2222222" >> c.txt$ git commit -am "error commit"# 465f512 新增 d.txt 并写入”3333333“$ echo "3333333" >> d.txt$ git commit -am "add d.txt"
复制代码


现在的提交历史是这样的:


* 465f512 (HEAD -> main) add d.txt* d46cf7d error commit* 1a7bdea add c.txt* 3a9e49f (branch_b) 更新 b.txt* 878244b (origin/branch_b) add b.txt* 9d4bbdc (origin/main) first commit(END)
复制代码


看下 c.txt 文件内容


$ cat c.txt11111112222222
复制代码


现在假设发现第五次修改有错误,想要恢复第五次修改,但是要保留第六次修改


这个时候就要使用 git revert 命令


git revert -n d46cf7d
复制代码



我们再提交一下:git commit -m "移除第 5 次提交内容"


git 提交历史就会变成下面这样:



最佳实践


在确认要回滚的版本之后,如果别人没有最新提交,那么就可以直接用 reset 命令进行版本回退,否则,就可以考虑使用 revert 命令进行还原修改,不能影响到别人的提交。

配置 git

配置范围

系统级配置

/etc/gitconfig文件,很少用


$ git config --system user.name "author"$ git config --system user.email "author@qq.com"
复制代码

全局设置

~/.gitconfig 文件


即设置本地所有仓库的代码提交作者


$ git config --global user.name "author"$ git config --global user.email "author@qq.com"
复制代码

项目设置

项目目录下的 .git/config 文件


本地既有公司仓库又有个人仓库时,公司仓库使用公司账号,个人仓库使用个人账号


进入个人仓库设置个人账号信息


$ cd rocketmq$ git config user.name "girl"$ git config user.email "girl@qq.com"
复制代码


进入公司仓库设置公司账号信息


$ cd apollo$ git config user.name "boy"$ git config user.email "boy@qq.com"
复制代码

常用配置

  • user.name

  • user.email

  • alias

  • commit.template

  • core.excludesfile

  • color.ui


可以把经常使用的几个命令设置下别名


$ git config --global alias.co checkout$ git config --global alias.br branch$ git config --global alias.ci commit$ git config --global alias.st status$ git config --global alias.last 'log -1 HEAD'
复制代码

统计信息

查看 git 上的个人代码量

--numstat


--stat 类似,会输出每个文件作者添加和删除的行数。但对二进制文件无效。</br>比如 c.txt 文件,maike 添加了三行代码,删除了一行代码,执行以下命令观察输出结果:


$ git log --author="maike" --pretty=tformat: --numstat3    1    [path]/c.txt
复制代码


awk


我们可以借助 awk 命令来分割上个命令的输出内容并累加每个文件的增删数量,并计算出总添加行数、总删除行数、累计代码总数:


$ git log --author="maike" --pretty=tformat: --numstat | awk '{ add += $1; subs += $2; loc += $1 - $2 } END { printf "added lines: %s, removed lines: %s, total lines: %s\n", add, subs, loc }' -
added lines: 281, removed lines: 1, total lines: 280
复制代码

统计每个人增删行数

上面那个统计是指定了 author,如果统计每个人的代码增删行数,只需要外层加个循环即可


$ git log --format='%aN' | sort -u | while read name; do echo -en "$name\t"; git log --author="$name" --pretty=tformat: --numstat | awk '{ add += $1; subs += $2; loc += $1 - $2 } END { printf "added lines: %s, removed lines: %s, total lines: %s\n", add, subs, loc }' -; done
maike added lines: 281, removed lines: 1, total lines: 280
复制代码

查看仓库提交者排名

git log --pretty='%aN' 输出项目所有的提交记录的提交作者信息</br>sort 按作者名称排序</br>uniq -c 去重并计算重复出现的次数</br>sort -k1 -nr 按第一列即名称重复的次数的数值大小从大到小排序</br>head -n 10 只显示 10 行</br>


这里我的示例项目不好弄,我在开源框架 Hystrix 项目中执行一下


$ git log --pretty='%aN' | sort | uniq -c | sort -k1 -nr | head -n 101061 Matt Jacobs 519 Ben Christensen  75 dmgcodevil  58 Bob T Builder  52 Justin Ryan  30 builds  24 Roman Pleshkov  13 Dave Brosius  12 Dave Ray  12 Christoph Seibert
复制代码

谢谢

看到这里,很感谢您能花时间阅读本文,如果本文对您有帮助,请不要吝啬您的鼓励和支持,有空评论点赞收藏三连,没空点赞也好,让作者保持继续创作的欲望。谢谢!

参考资料

  1. https://git-scm.com/book/en/v2

  2. https://www.atlassian.com/git/tutorials/learn-git-with-bitbucket-cloud

  3. https://www.runoob.com/linux/linux-comm-read.html

  4. https://www.runoob.com/linux/linux-comm-awk.html

  5. https://www.runoob.com/linux/linux-comm-sort.html

  6. https://www.runoob.com/linux/linux-comm-uniq.html

  7. https://www.runoob.com/linux/linux-comm-head.html

发布于: 刚刚阅读数: 3
用户头像

攻城狮MK

关注

道阻且难,行则将至 2018.11.11 加入

公众号:攻城狮MK

评论

发布
暂无评论
git 使用总结