常用速查

git commit

-m 直接在当前输入中写评论
-am 不用add直接跳过添加到暂存区的步骤
—amend 修改最近一次提交内容 或 提交评论

git push

git push 当前分支推送到远程的对应分支
git push -u , -u 和 —set-upstream 将本地分支跟踪远程,下次就可以直接git push

git branch

git branch - 列举本地所有分支
git branch - 新建一个分支,但不会主动切过去
git branch -m - 将当前分支改为新名字
git branch -M - 将当前分支强行改为新名字,哪怕已经存在这样的分支了
git branch -u - 将本地分支和远程分支挂钩
git branch -vv 可以查看本地设置的所有跟踪分支
git branch -d 删除本地分支,如果没有被merged的内容会要求用-D
git branch -v (verbose的缩写 冗长啰嗦的)
git branch -a (既包含local的分支,又包含remote的分支)

需要重点注意的一点:是这些数字的值来自于你从每个服务器上最后一次抓取的数据。这个命令并没有连接服务 器,它只会告诉你关于本地缓存的服务器数据。如果想要统计最新的领先与落后数字,需要在运行此命令前抓取 所有的远程仓库。可以像这样做:$ git fetch —all; git branch -vv

git reset

git reset会将暂存区的内容都移动到非暂存区
git reset HEAD — 将具体的文件内容从暂存区动到非暂存区
git reset —hard会将暂存区和非暂存区的修改丢废弃掉
git reset —hard 除了丢弃暂存区和工作区改动,还会将提交改为commit-id的内容
git reset HEAD 和 git reset一样的含义
git reset HEAD~2将工作区内容重置为前两个提交的(可以理解为-2,当前索引为0,撤销更早的2个提交)

  • 一个分支如果经过三个分支合并,那么HEAD^1指向第一个父级,依此类推
  • 一般而言回撤到更早提交用~就好了,它等同于HEAD~1
  • 直接git reset HEAD没有意义,以当前代码为基准

—hard 工作区内容切回到指定的commit,工作区和暂存区变更都丢失
—soft 历史切回到指定的commt,暂存区多了丢失版本的内容,工作区内容仍旧在

例如HEAD~2那么 HEAD~1和HEAD的修改内容会在暂存区

—fix 清空暂存区,保留工作区

下面两个命令是一样效果,如果只有一个文件要撤回,第三个命令也一样(默认—soft)

  • git reset HEAD —
  • git restore —staged
  • git reset

    git checkout

    git checkout 切换到某个分支
    git checkout -b 新建一个分支并且切换过去
    git checkout — 工作区的改动废弃掉

    git rebase

    git branch -i 将当前分支代码rebase到更早的节点,会弹出commit-id之后的提交,然你选择针对这些提交要做合作操作。
    image.png

    git log

  • —all 所有分支上的提交日志,而非当前HEAD指向的分支

  • —graph 使用树形结构描绘提交历史
  • —oneline 单行展示提交历史
  • 具体某个分支上的日志
  • -n 2 | -2 最近的两次日志

    git diff

    git diff 比较暂存区和非暂存区
    git diff —cached 比较暂存区和HEAD, —staged 也是一样的效果
    git diff commitid1 commitid2 — filename 比较不同提交的文件差异(可以跨分支)

    git restore

    文件在暂存区,可以通过git restore —staged 移动到非暂存区
    文件在废暂存区,可以通过git restore 来撤销更改

    git remote

    git remote add origin
    git remote -v 显示所有的远程仓库

    git stash

    git stash 将工作区的改动藏起来
    git stash list 查看藏起来的内容(会有多次)
    git stash pop 将藏起的内容放出到工作区,删除藏起的内容
    git stash apply 将藏起的内容放出到工作区,仍旧保留藏起的内容

    Kaizen meta

    来源

    注:搞懂了Pro Git 第二版前三章,结合工作基本够用了,极客时间的课程对于有开发经验的人而言太鸡肋了。
    极客时间 「玩转Git三剑客」;《Pro Git 第二版》下载地址.pdf)

    用时

    4月23日 364分钟 6小时 4分钟

  • 40分钟 [1] 第1章-6个视频

  • 43分钟 [2] 第1章
  • 53分钟 [1] 第1章-5个视频
  • 14分钟 [1] 第1章-2个视频
  • 132分钟 [2] 第2章
  • 82分钟 [2] 第3章(还剩1节)

4月24日 397分钟 6小时53分钟

  • 23分钟 [2]第3章
  • 154分钟 问题研究:常用命令
  • 151分钟 问题研究:rebase
  • 69分钟 [1]第二部分
  • 整理文档 15’

    玩转Git三剑客

    01-理论介绍 12:40-12:45

    vcs本地不可以管理整个版本库
    分布式vcs克服了上述问题
    因为分部署vcs要收费所以linus开发了git
    课程包含git、GitHub、gitlab三部分,很多公司基于gitlab进行了二次开发

    02 git安装 12:46

    03 git最小配置 12:46-12:53

    1. git config --local/global/system user.name 'tangyefei'
    2. git config --local/global/system user.email 'a@b.com'
    3. git config --list --local/global/system
    注意到个人的global有一个个人账号的配置,local中(具体项目文件夹中)有一个公司邮箱配置
    🤔 作者的说法是,安装时候会默认配置的system的
    🤔 那么个人mac上global和local分别都是什么时候配置的呢

    04 新建git仓库 12:54-13:01

    基于config命令配置了name和email然后新建了readme并进行提交
    git init在当前项目文件夹下生成了.git文件夹
    commit 的时候会提示出来在什么 分支上 提交节点的hash码
    git log可以看提交日志,包含hash码、提交人的信息、提交日期,提交备注
    🤔 这些概念都是什么意思呢:root-commit? master是默认的吗?HEAD又是什么意思
    🤔 git log展示的是所有分支的提交信息吗
    image.png
    image.png

    05 仓库中添加文件 13:01-13:11

    git add过的就是暂存区
    git add -u 添加了所有在暂存区的文件
    🤔 跟git add -A的区别是什么,推荐用哪个,add -u对于新文件不能添加

    06 文件重命名 13:12-13:20

    bash中文件重命名的方法是 mv target destination
    直接删除文件的结果是一个新增一个删除
    如果把新增的git add,删除的直接git rm,git是比较智能,能够识别是重命名的
    git reset —hard 暂存区、工作区所有东西都会被清理掉
    一个命令来达成上述目的 git mv target destination
    🤔 实测下来,git已经非常只能直接 add -a 就能识别rename

    07 git log 14:18-14:23

    介绍了log的多种用法,这里不细究,在书籍中再详细实践:
    git log —oneline
    git log -n4 (指定最近4个)
    git log —all(所有分支)
    git log —graph
    git log <具体分支名>
    git help —web log web中查看

git commit -am 直接绕过add进行提交 (注意:新增加的文件不会被add起来,还是需要-A)
git branch -av 🤔

08 gitk 14:24-14:27

patch vs tree 模式
commiter vs author,如果你cherry-pick了别人的代码,两个概念就有用了
后续或基于gitk做更多的围绕git的操作

09 .git目录 14:51-15:14

HEAD的内容只想当前所工作的分支:

  1. ref: refs/heads/master

config 是提交的配置信息,和git config —local —list 看到的是一样的

  1. [core]
  2. repositoryformatversion = 0
  3. filemode = true
  4. bare = false
  5. logallrefupdates = true
  6. ignorecase = true
  7. precomposeunicode = true
  8. [user]
  9. name = tangyefei
  10. email = tyfccsu@gmail.com

用cat可直接查看内容(区别于vim跳入另外一个界面)
resf下有heads和tags,heads中是各个分支的名称的文件,内容是hash值; 拷贝hash部分内容可以查看hash标识的是什么内容,下面是heads/master内的部分内容,结果是commit

  1. git cat-file -t 963f193a

tags则是标签,相应的在tags每个标签会对应一个具体的文件,拷贝文件内容执行上述命令之后,结果是tag,修改参数 -t 为 -p,可以看到它它的输出内容,说明它又指向了commit

  1. $ git cat-file -p 55815dc1
  2. object 6b879903898deb6a9e0593b1d9809fd2741c8a56
  3. type commit
  4. tag t01

进入到objects目录,可以看到很文件夹,还有一个pack(是松散文件打包后的存放地址),随便进入一个文件夹,比如 19,然后看到它里面有别的文件,将19和新的文件名拼接起来,然后cat-file -t 可以看到它是一个tree,使用cat-file -p可以看到他的内容是blob文件的描述

  1. $ git cat-file -p 19bb7df5fd190f9a2270b5c419a71dacc2f2ae7c
  2. 040000 tree d87d8abfbe31bbb0b9e488e0fe7ed5274b100565 images
  3. 100644 blob 2e55ea39d153cabcb587d3c17aa357a36eaa9d37 index.html
  4. 100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 readme
  5. 040000 tree 694d00c9b7cc9f941e2c1c9b729a2674727592b3 styles

随便找一个blob类型(文件)的在进行cat-file -p,则可以看到文件的内容。

10 commit\tree\blob三个对象之间的关系 15:14-15:24

commit中包含了tree的信息
tree中包含了文件夹和blob信息
blob的信息只想了文件内容
具体验证步骤:

  • git log查看提交历史
  • cat-file -p 查看某个提交的内容
  • 找到提交中的tree的id,然后cat-file(每个提交的的tree的id都不同,代表不同节点时的快照)
  • cat-file -p 查看tree 的内容
  • cat-file -p 查看tree 的 子tree 或 blob的内容
    • tree 会继续展开
    • blob 会输出文件内容

      11 小练习 tree的个数 15:25-15:36

echo “hello world” > readme
可以快捷输入内容到文件中
find .git/objects -type f
可以查找文件夹下类型为文件的文件

作者在一个全新的仓库下面新建了文件夹docs和文件readme,那么请问在git add 和 git commit之后,objects下会有多少个对象。

结论是:

  • add的时候,可以在objects下找到一个blob(内容即readme的内容)
  • commit了之后,可以在objects下找到4个文件
    • 一个commit文件
    • 2个tree文件
    • 一个blob文件

1个提交(commit) 指向 1个文件树
1个文件树包含另外一个文件树doc
另外一个文件树指向了1个blob文件

12 分离头指针情况下的注意事项 16:51-16:59

detached HEAD
工作在没有分支的情况下,如果你改了代码并且提交了,再切换到其他分支,很可能会因为没有关联到具体的分支,后续内容丢失掉,git 会建议你将刚刚做的提交,切换到一个新的的分支,例如:
git checkout newbranch cd3b92

13 进一步理解HEAD和branch 17:00-17:05

HEAD指向的可以使detached commit,也可以指向一个分支。指向分支时,本质上也是这个分支的最后一个提交。
可以通过git diff 比较两个commit的内容,使用HEAD可以标识当前指向的commit,使用HEAD^1代表父亲,使用HEAD~2代表和更早的两次提交的差别。
🤔 具体结合书籍再做实践。

14 删除无用分支 17:58-18:08

更新到git branch 中

15 怎么修改最近一次提交的message 18:06-18:08

补充到git commit 中

16 怎么修改老旧的commit的message 18:08-18:21

补充到git rebase中(git rebase -i commit-id)

17 怎么把多个commit整理成一个 18:21-18:25

仍旧是基于 git rebase -i commit-id,问询面板中,在希望合并的提交上使用squash

18 怎么把间隔的几个commit整理成一个 18:25-18:30

仍旧是基于 git rebase -i commit-id,问询面板,差别在于需要挪动提交的出现顺序。例如顺序提交为:abc,希望c合并到a,修改后是下面这样:

pick a squash c pick b

19 比较暂存区和HEAD 18:30-18:34

补充到git diff中

20 比较工作区和暂存区的差异 18:34-18:37

补充到git diff中

21 如何让暂存区恢复成和HEAD的一样?18:38-18:39

补充到git reset HEAD(跟git reset一样的效果)

22 如何让工作区的内容恢复成和暂存区一样的?18:39-18:43

补充到 git checkout —

23 如何取消暂存部分文件的修改 18:43-18:44

补充到git reset HEAD — file

24 消除最近的几次提交 18:44-18:45

补充到 git reset —hard commit-id 在这个提交之后的提交都会丢失,暂存区和工作区的改动都丢失

25 看看不同提交的指定文件的差异 18:45-18:49

补充道 git diff commitid1 commitid2 — filename

26 正确删除文件的方法 18:49-18:51

补充到 git rm filename,比起 工作路径删除 + 添加到提交,更快

27 开发中临时加塞了紧急任务怎么处理?18:51-18:54

补充到 git stash 中

28 如何指定不需要Git管理的文件?18:54-19:00

.gitignore 略

29 如何将Git仓库备份到本地?19:00-19:05

补充到git remote 和 git push

Git Pro 第二版

序1、序2、献词 13:22-13:29

作者写第一版本的时,还是github员工,4人为它在工作,大概几千人用github
写作第二版时(2014年),已经过关了1000万个项目,500万注册用户,230名员工。
第二名作者也是git主要实现贡献者,也在github工作。
[2] 起步 17pages 13:30-版本控制历史-13:36-git历史-13:38

第1章 起步

发展历史:13:29-13:35

  1. 文件拷贝(容易混淆和出错)
  2. 本地版本控制系统(vcs 数据库记录改动 )
  3. 集中式版本控制系统(cvcs 不同系统上的开发者写作,缺点是服务器挂了很灾难)
  4. 分布式版本控制系统(dvcs distributed 客户端也能包含完整的镜像,不怕任何一端的故障)

    git历史:13:35-13:40

    商业公司跟Linux合作结束不将BitKeeper免费提供给Linux说会用,林纳斯开发出了自己的版本

    git基础:13:40-13:54

    保存每个版本的快照,而非初始文件+差异,这时跟几乎所有其他的本本控制系统的重要区别;
    几乎所有工作都可以本地进行,无需因为网络受到限制;
    以SHA-1散列计算所有数据存储时的校验和,它是由40个16进制组成的字符串,基于git文件的内容和目录结构计算出来,git数据库中保存等信息都以文件内容的hash值来索引;
    执行的git操作只添加数据,很难出现不可逆、或者以任何方式清除数据,「撤销操作」部分有深度讨论
    modifyed staged commited ,分别标识文件修改了,做了标记用于包含在下次提交,已经保存本地数据库,由此引入桑工作区域的概念:

  5. git仓库

  6. 工作目录
  7. 暂存区域

image.png
【Git 仓库】目录是 Git 用来保存项目的元数据和对象数据库的地方。这是 Git 中最重要的部分,从其它计算机克隆 仓库时,拷贝的就是这里的数据。
【工作目录】是对项目的某个版本独立提取出来的内容。这些从 Git 仓库的压缩数据库中提取出来的文件,放在磁盘 上供你使用或修改。
【暂存区域】是一个文件,保存了下次将提交的文件列表信息,一般在 Git 仓库目录中。有时候也被称作`‘索 引’’,不过一般说法还是叫暂存区域。

Git基础会进一步了解这些状态的细节,并根据文件状态实现后续操作,已经怎样「🤔 跳过暂存直接提交」。

命令行 13:54-13:55

GUI模式不能提供所有功能;掌握了命令行使用GUI不会遇到什么困难,反之则不成立。

安装git

初次安装git前的配置 13:56-14:01

git config 用于设置和查看设置
/etc/gitconfig 系统级别的仓库通用配置
~/.gitconfig~/.config/git/config 当前用户
.git/config 当前仓库的配置
每个配置覆盖前面的配置
使用 git config —global user.name 形式配置name和email
使用git config —list 查看所有配置,如果有重复变量名则使用最后一个
使用 git config 来查看具体的配置项

获得帮助 14:01-14:05

  1. $ git help <verb>
  2. $ git <verb> --help
  3. $ man git-<verb>

例如想看config的命令可以使用 git help config

第二章 Git基础

6’ 获取git仓库 17:09-15

git init 在现有文件夹中初始仓库;git clone现有仓库

54’ 记录每次变更到仓库 17:15-22-24-25-30 40-49-53-18:01-04-12-14

git rm 某个文件后会讲其当做未来跟踪的文件,跟rm本质上应该是一样的吧,测试下来区别在于前者将remove行为添加到暂存区了,后者还要手动add一次。
image.png

检查当前文件状态:git status

跟踪新文件:git add

暂存已修改文件

新增的文件 和 此前就存在仓库的文件,在git status时候都会提示需要进行git add才能被添加到暂存区。区别是展示在不同的区块下。

$ git status On branch master Changes not staged for commit: (use “git add …” to update what will be committed) (use “git restore …” to discard changes in working directory) modified: readme

Untracked files: (use “git add …” to include in what will be committed) CHANGELOG.md

另外值的注意的,如果我们将文件加入到暂存区后,又继续修改,那么git status会显示在暂存区 和 工作目录下都有这个文件,暂存区中的是较早的版本,工作目录是后续再次修改的版本。

状态简览

git status -s | —short

  1. $ git status -s
  2. M readme
  3. ?? CHANGELOG.md
  4. $ ga
  5. $ git status -s
  6. A CHANGELOG.md
  7. M readme
  8. $ git status -s
  9. AM CHANGELOG.md
  10. M readme

M: modified file
??: untracked file
A: added new file to staging
M: added modified to staging
AM: added to staging, but changed another version in workspace

忽略文件: .gitignore

• 所有空行或者以 # 开头的行都会被 Git 忽略。
• 可以使用标准的 glob 模式匹配( glob 模式即 shell 所使用的简化了的正则表达式)。
• 匹配模式可以以(/)开头防止递归。
• 匹配模式可以以(/)结尾指定目录。
• 要忽略指定模式以外的文件或目录,可以在模式前加上惊叹号(!)取反

glob模式的介绍:星号()匹配零个或多个任意字符;[abc] 匹配任 何一个列在方括号中的字符(这个例子要么匹配一个 a,要么匹配一个 b,要么匹配一个 c);问号(?)只匹配 一个任意字符;如果在方括号中使用短划线分隔两个字符,表示所有在这两个字符范围内的都可以匹配(比如 [0-9] 表示匹配所有 0 到 9 的数字)。使用两个星号() 表示匹配任意中间目录,比如a/**/z 可以匹配 a/z, a/b/z 或 a/b/c/z等。

查看已暂存和未暂存的修改: git diff vs git diff —staged

git diff 比较的为添加到暂存区的内容,如果你add了之后,可能什么也看不到
git diff —staged / —cached 比较的是暂存区所添加的内容
git difftool —tool-help 可以看支持哪些diff插件

提交更新 git commit

直接git commit会打开文本编辑器来输入本次说明
通过增加 -m 参数可以在命令行同一行内输入说明

跳过使用暂存区 git commit -a

增加-a可以跳过git add的操作

移除文件 git rm

个人理解,git rm等同于rm和git add的操作
如果内容已经被添加到暂存区了,直接git rm是会报错的,提示需要-f参数强行rm

移动文件 git mv

一个命令抵三个命令(对于第二条,git add README.md应该是一样的意思)

  1. $ mv README.md README
  2. $ git rm README.md
  3. $ git add README

15’ 查看提交历史 18:19-34

git log提供的有用参数这里简单介绍下(绿色是常用的) 🤔 大概—online是新推出的没在下表中
image.png
还有一些选项用于筛选那些指定条件的调,例如 - 可以限定只看过去几次提交,—since —until 可以限定某短时间内的提交,—author可以显示指定作者的提交,—grep可以搜索提交的关键字,-S可以用查找添加或者删除某些特定字符串的提交(git log -Sfunction_name),还有一些选项可以值看某些路径下的提交。
下面是一个复杂的例子,查看 Git 仓库中,2008 年 10 月期间,Junio Hamano 提交的但未合并的测试文 件,可以用下面的查询命令:

  1. $ git log --pretty="%h - %s" --author=gitster --since="2008-10-01" \
  2. --before="2008-11-01" --no-merges -- t/
  3. 5610e3b - Fix testcase failure when extended attributes are in use
  4. acd3b9e - Enhance hold_lock_file_for_{update,append}() API
  5. f563754 - demonstrate breakage of detached checkout with symbolic link
  6. HEAD
  7. d1a43f2 - reset --hard/read-tree --reset -u: remove unmerged new paths
  8. 51a94af - Fix "checkout --track -b newbranch" on detached HEAD
  9. b0ad11e - pull: allow "git pull origin $something:$current_branch" into an
  10. unborn branch

15’ 撤销操作 18:34-49

这里会介绍少有的几个因为工作失误而导致的工作丢失的地方。

case1 漏提交了 或 备注写错了

提交完才发现漏掉文件没有添加,或者提交信息写错了
git commit —amend
🤔 自己连续两次amend,第二次修改备注时,发现备注信息不是上一次的,而是第一次的

case2 不要暂存了

添加到暂存区域后发现要取消
git reset HEAD
它和 reset —hard的危险处理不同,只是会讲内容从暂存区移动到未暂存区

case3 不要改动了

git checkout —
它会撤销你对文件的修改(非暂存区),它是一个危险的命令,会覆盖你本地的文件修改。

总结

几乎已经提交的任何东西都是可以恢复的,甚至被删除的分支重点提交或使用—amend覆盖的提交。唯一不能恢复的是,你未提交的东西。

20’ 远程仓库的使用 18:49-19:09

你可以有多个远程仓库,不同的仓库有不同的权限。例如你fork了一个仓库,仍旧可以从原仓库中拉取更新。

查看远程仓库

git remote
git remote -v

添加远程仓库

git remote add

从远程仓库中抓取和拉取

git fetch [remote-name]
会访问远程仓库拉取你还没有的数据,默认clone的仓库origin指向了你克隆的地址,注意fetch会拉取到你本地仓库,它不会自动合并或修改你当前的工作(🤔 如果你拉取了,然后其他人改了,你这时候fetch,不就会有冲突么)。如果将本地分支和远程峰值设定跟踪关系,可以使用git pull命令来拉取,默认是master跟踪远程的master(🤔 具体git pull经常提示的内容是什么意思)

推送到远程仓库

git push origin master

查看远程仓库

git remove show origin
可以提供非常详细的关于分支的信息:什么分支是新的(new),什么分支远程被删除了(stale-不新鲜的,使用prune修建本地的过时分支),你配置了哪些本地和远程分支的跟踪可以直接使用git push或git pull。

  1. $ git remote show origin
  2. * remote origin
  3. URL: https://github.com/my-org/complex-project
  4. Fetch URL: https://github.com/my-org/complex-project
  5. Push URL: https://github.com/my-org/complex-project
  6. HEAD branch: master
  7. Remote branches:
  8. master tracked
  9. dev-branch tracked
  10. markdown-strip tracked
  11. issue-43 new (next fetch will store in remotes/origin)
  12. issue-45 new (next fetch will store in remotes/origin)
  13. refs/remotes/origin/issue-11 stale (use 'git remote prune' to remove)
  14. Local branches configured for 'git pull':
  15. dev-branch merges with remote dev-branch
  16. master merges with remote master
  17. Local refs configured for 'git push':
  18. dev-branch pushes to dev-branch
  19. (up to date)
  20. markdown-strip pushes to markdown-strip
  21. (up to date)
  22. master pushes to master
  23. (up to date)

远程仓库的移除和重命名

git remote rename oldname newname
git remote rm notwanted

18’ 打标签 19:10-19:28

给提交打上标签,以示重要,例如发布版本 v1.0。

列出标签: git tag

仅仅对于某个匹配的版本感兴趣可以 git tag -l ‘v1.8.5*’

  1. $ git tag -l 'v1.8.5*'
  2. v1.8.5
  3. v1.8.5-rc0
  4. v1.8.5-rc1
  5. v1.8.5-rc2
  6. v1.8.5-rc3
  7. v1.8.5.1
  8. v1.8.5.2
  9. v1.8.5.3
  10. v1.8.5.4
  11. v1.8.5.5o'm

创建标签

Git 使用两种主要类型的标签:轻量标签(lightweight)与附注标签(annotated)。
一个轻量标签很像一个不会改变的分支 - 它只是一个特定提交的引用。

轻量标签

git tag v1.0.0

附注标签

附注标签是存储在 Git 数据库中的一个完整对象。它们是可以被校验的;其中包含打标签者的名字、电子 邮件地址、日期时间;还有一个标签信息;并且可以使用 GNU Privacy Guard (GPG)签名与验证。通常建议创建附注标签,这样你可以拥有以上所有信息;
但是如果你只是想用一个临时的 标签,或者因为某些原因不想要保存那些信息,轻量标签也是可用的。

需要指定-a选项,-m制定了存储在标签中的信息(-m不是必须的)。个人理解就是辅助标签可以输入一些描述信息,其他地方和轻量标签没有差异。上述所说的更多差异没体会到。

  1. $ git tag -a v1.4 -m 'my version 1.4'
  2. $ git show v1.4
  3. tag v1.4
  4. Tagger: Ben Straub <ben@straub.cc>
  5. Date: Sat May 3 20:19:12 2014 -0700
  6. my version 1.4
  7. commit ca82a6dff817ec66f44342007202690a93763949
  8. Author: Scott Chacon <schacon@gee-mail.com>
  9. Date: Mon Mar 17 21:52:11 2008 -0700
  10. changed the version number

后期打标签

git tag -a v1.2 9fceb02

共享标签

git push 命令并不会传送标签到远程仓库服务器上。在创建完标签后你必须显式地推送标签到共 享服务器上。这个过程就像共享远程分支一样 - 你可以运行 git push origin [tagname]。
如果想要一次性推送很多标签,也可以使用带有 —tags 选项的 git push 命令。这将会把所有不在远程仓库 服务器上的标签全部传送到那里。

检出标签

标签不同于分支,可以来回移动,它固定在某个提交上。
如果相机与某个特定的标签来创建新分支,可以使用 git checkout -b [branchname] [tagname] 。
本质上 tagname 和 使用commit的hash值一样的。

4’ Git别名 19:28-32

提供了简化git命令的技巧,下面只是例子,可以更多发挥

  1. $ git config --global alias.co checkout
  2. $ git config --global alias.br branch
  3. $ git config --global alias.ci commit
  4. $ git config --global alias.st status
  5. $ git config --global alias.unstage 'reset HEAD --'
  6. # 下面两个命令是等价的
  7. $ git unstage fileA
  8. $ git reset HEAD -- fileA

如果想执行外部命令,则需要增加 !

  1. $ git config --global alias.visual '!gitk'

第三章 Git分支

3.1 分支简介 7p 21:04-21:30

在进行提交操作时,Git 会保存一个提交对象(commit object)。
首次提交产生的提交对象没有父对象,普通提交操作 产生的提交对象有一个父对象,而由多个分支合并产生的提交对象有多个父对象。
我们假设现在有一个工作目录,里面包含了三个将要被暂存和提交的文件。

  1. $ git add README test.rb LICENSE
  2. $ git commit -m 'The initial commit of my project'

Git 仓库中有五个对象:三个 blob 对象(保存着文件快照)、一个树对象(记录着目录结构和 blob 对象 索引)以及一个提交对象(包含着指向前述树对象的指针和所有提交信息)。
image.png
做些修改后再次提交,那么这次产生的提交对象会包含一个指向上次提交对象(父对象)的指针
image.png
Git 的分支,其实本质上仅仅是指向提交对象的可变指针。Git 的默认分支名字是 master。在多次提交操作之 后,你其实已经有一个指向最后那个提交对象的 master 分支。它会在每次的提交操作中自动向前移动。
image.png

分支创建

使用下面的命令可以快速创建一个分支,这会在当前所在的提交对象上创建一个指针。

  1. $ git branch testing

image.png

Git 又是怎么知道当前在哪一个分支上呢?它有一个名为 HEAD 的特殊指针,指向当前所 在的本地分支(译注:将 HEAD 想象为当前分支的别名)。在本例中,你仍然在 master 分支上。因为 git branch 命令仅仅 创建 一个新分支,并不会自动切换到新分支中去。
image.png

你可以简单地使用 git log 命令查看各个分支当前所指的对象。提供这一功能的参数是 —decorate。

  1. $ git log --oneline --decorate
  2. f30ab (HEAD, master, testing) add feature #32 - ability to add new
  3. 34ac2 fixed bug #1328 - stack overflow under certain conditions
  4. 98ca9 initial commit of my project

分支切换

  1. $ git checkout testing

image.png

这样做的好处可以体现为,不同分支上发展各自的版本,切回分支做了这两件事:
1)移动HEAD到对应分支所指向的提交节点
2)将工作目录恢复成对应提交节点的文件快照的内容

  1. $ vim test.rb
  2. $ git commit -a -m 'made a change'

image.png

注:如果你当前工作空间有文件没被提交,将不能完成切换。

如果我们继续master上进行提交,则项目会出现分叉。所有上述的工作用到的三个命令总结为:

  • branch 新建分支
  • checkout 切换分支
  • commit 提交分支上的内容

注:git checkout -b 是 git branch 和 git checkout 的简略写法
image.png

你可以简单地使用 git log 命令查看分叉历史。运行 git log --oneline --decorate --graph --all,它会输出你的提交历史、各个分支的指向以及项目的分支分叉情况。

  1. $ git log --oneline --decorate --graph --all
  2. * c2b9e (HEAD, master) made other changes
  3. | * 87ab2 (testing) made a change
  4. |/
  5. * f30ab add feature #32 - ability to add new formats to the
  6. * 34ac2 fixed bug #1328 - stack overflow under certain conditions
  7. * 98ca9 initial commit of my project

3.2 分支的新建与合并 9p 21:30-21:46

本节基于日常开发的流程的例子,涉及三个分支多次提交和合并,是非常好的一个例子。

唯一记录一个图,即分支已经分叉的时候进行将issue53合并到master的:

Git 会使用两个分支的末端所指的快照(C4 和 C5)以及这两个分支的工 作祖先(C2),做一个简单的三方合并。和之间将分支指针向前推进所不同的是,Git 将此次三方合并的结果做了一个新的快照并且自动创建一个新的提 交指向它。这个被称作一次合并提交,它的特别之处在于他有不止一个父提交。

image.png
上面所的先前推进是更简单的一种case:将合并hotfix合并到master

  1. $ git checkout master
  2. $ git merge hotfix
  3. Updating f42c576..3a0874c
  4. Fast-forward
  5. index.html | 2 ++
  6. 1 file changed, 2 insertions(+)

image.png
注:合并有两种,一种是本地分支过时了合并master的内容;一种是修复问题了,将代码合并到master上线。
注:既然理解了分支本质上只是一些指针,修复了问题的分支,完全可以通过git branch -d删除掉,没有任何保留的意义。这也是为什么自己之前会觉得留着也许有用。

3.3 分支管理 2p 21:47-21:51

git branch 命令不只是可以创建与删除分支。如果不加任何参数运行它,会得到当前所有分支的一个列表:

  1. $ git branch
  2. iss53
  3. * master
  4. testing

注: master 分支前的 * 字符:它代表现在检出的那一个分支(也就是说,当前 HEAD 指针所指向的分支)。

如果需要查看每一个分支的最后一次提 交,可以运行 git branch -v 命令:

  1. $ git branch -v
  2. iss53 93b412c fix javascript issue
  3. * master 7a98805 Merge branch 'iss53'
  4. testing 782fd34 add scott to the author list in the readmes

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

  1. $ git branch --merged
  2. iss53
  3. * master

在这个列表中分支名字前没有 * 号的分支通常可 以使用 git branch -d 删除掉;你已经将它们的工作整合到了另一个分支,所以并不会失去任何东西。

查看所有包含未合并工作的分支,可以运行 git branch —no-merged:

  1. $ git branch --no-merged
  2. testing

这里显示了其他分支。因为它包含了还未合并的工作,尝试使用 git branch -d 命令删除它时会失败,可以采用-D
强制删除它:

  1. $ git branch -d testing
  2. error: The branch 'testing' is not fully merged.
  3. If you are sure you want to delete it, run 'git branch -D testing'.

3.4 分支开发工作流 4p 21:51-21:59

常见的利用 分支进行开发的工作流程,也是典型的工作模式,你可以根据项目实际情 况选择一种用用看。

长期分支

比如只在某个分支(例如 master)上保留完全稳定的代码——有可能仅 仅是已经发布或即将发布的代码。他们还有一些名为 develop 或者 next 的平行分支,被用来做后续开发或者 测试稳定性——这些分支不必保持绝对稳定,但是一旦达到稳定状态,它们就可以被合并入 master 分支了。
image.png

特性分支

特性分支是一种短期分支,它被用来实现单一特性或其相关工作。
考虑这样一个例子,你在 master 分支上工作到 C1,这时为了解决一个问题而新建 iss91 分支,在 iss91 分 支上工作到 C4,然而对于那个问题你又有了新的想法,于是你再新建一个 iss91v2 分支试图用另一种方法解决 那个问题,接着你回到 master 分支工作了一会儿,你又冒出了一个不太确定的想法,你便在 C10 的时候新建 一个 dumbidea 分支,并在上面做些实验。你的提交历史看起来像下面这个样子:
image.png

你决定使用第二个方案来解决那个问题,即使用在 iss91v2 分支中方案;另外,你 将 dumbidea 分支拿给你的同事看过之后,结果发现这是个惊人之举。这时你可以抛弃 iss91 分支(即丢弃 C5 和 C6 提交),然后把另外两个分支合并入主干分支。最终你的提交历史看起来像下面这个样子:
image.png

3.5 远程分支 8p 22:28-22:55

你可以通过 git ls-remote (remote) 来显 式地获得远程引用的完整列表;
或者通过 git remote show (remote) 获得远程分支的更多信息。
更常见的做法是利用远程分支跟踪。

远程分支

远程分支以 (remote)/(branch) 形式命名。例如,如果你想要看你最后一次与远程仓库 origin 通信时 master 分支的状态,你可以查看 origin/master 分支。你与同事合作解决一个问题并且他们推送了一个 iss53 分 支,你可能有自己的本地 iss53 分支;但是在服务器上的分支会指向 origin/iss53 的提交。

如果你在本地的 master 分支做了一些工作,然而在同一时间,其他人推送提交到 git.ourcompany.com 并 更新了它的 master 分支,那么你的提交历史将向不同的方向前进。也许,只要你不与 origin 服务器连接,你 的 origin/master 指针就不会移动。
image.png

我们假定你有另一个内部 Git 服务器,仅用于你的 sprint 小组的开 发工作。这个服务器位于 git.team1.ourcompany.com。你可以运行 git remote add 命令添加一个新的 远程仓库引用到当前的项目,将这个远程仓库命名为 teamone,将其 作为整个 URL 的缩写。

image.png
可以运行 git fetch teamone 来抓取远程仓库 teamone 有而本地没有的数据。因为那台服务器上现 有的数据是 origin 服务器上的一个子集,所以 Git 并不会抓取数据而是会设置远程跟踪分支 teamone/master 指向 teamone 的 master 分支。
image.png

推送

当你想要公开分享一个分支时,需要将其推送到有写入权限的远程仓库上。本地的分支并不会自动与远程仓库同 步 - 你必须显式地推送想要分享的分支。
如果希望和别人一起在名为 serverfix 的分支上工作,你可以像推送第一个分支那样推送它。运行 git push (remote) (branch):

  1. $ git push origin serverfix
  2. Counting objects: 24, done.
  3. Delta compression using up to 8 threads.
  4. Compressing objects: 100% (15/15), done.
  5. Writing objects: 100% (24/24), 1.91 KiB | 0 bytes/s, done.
  6. Total 24 (delta 2), reused 0 (delta 0)
  7. To https://github.com/schacon/simplegit
  8. * [new branch] serverfix -> serverfix

这里有些工作被简化了。Git 自动将 serverfix 分支名字展开为 refs/heads/serverfix:refs/heads/serverfix,那意味着,“推送本地的 serverfix 分支来更新远程仓 库上的 serverfix 分支。”

你也可以运行 git push origin serverfix:serverfix,它会做同样的事 - 相当于它说,“推送本地 的 serverfix 分支,将其作为远程仓库的 serverfix 分支”可以通过这种格式来推送本地分支到一个命名不相同的 远程分支。

如果并不想让远程仓库上的分支叫做 serverfix,可以运行 git push origin serverfix:awesomebranch 来将本地的 serverfix 分支推送到远程仓库上的 awesomebranch 分支。

下一次其他协作者从服务器上抓取数据时,他们会在本地生成一个远程分支 origin/serverfix,指向服务器 的 serverfix 分支的引用:

  1. $ git fetch origin
  2. remote: Counting objects: 7, done.
  3. remote: Compressing objects: 100% (2/2), done.
  4. remote: Total 3 (delta 0), reused 3 (delta 0)
  5. Unpacking objects: 100% (3/3), done.
  6. From https://github.com/schacon/simplegit
  7. * [new branch] serverfix -> origin/serverfix

要特别注意的一点是当抓取到新的远程跟踪分支时,本地不会自动生成一份可编辑的副本(拷贝)。换一句话 说,这种情况下,不会有一个新的 serverfix 分支 - 只有一个不可以修改的 origin/serverfix 指针。 可以运行 git merge origin/serverfix 将这些工作合并到当前所在的分支。如果想要在自己的 serverfix 分支上工作,可以将其建立在远程跟踪分支之上:

  1. $ git checkout -b serverfix origin/serverfix
  2. Branch serverfix set up to track remote branch serverfix from origin.
  3. Switched to a new branch 'serverfix'

注:

  • 直接git fetch origin会帮你同步远程的代码到本地
  • 要查看远程分支需要通过git ls-remote
  • 假定远程有个分支test,你需要自己git checkout test,并且将本地分支和远程进行了跟踪
    • Branch ‘test’ set up to track remote branch ‘test’ from ‘origin’
    • 即:上面例子是曾经较为复杂的写法,在出书8年之后已经是更简化了
  • 但是如果你远程没有分支,你直接git checkout anther是会报错的,需要增加-b,即git做了智能处理

🤔 有两个问题
1. 切出来的就git pull,git push就能直接使用了
2。 如果我在a分支上,执行git pull origin b 会怎么样?也可以拉,但是冲突不冲突就看情况了

🤔 在new-train-admin上做类似的checkout操作出现了不太理解的提示:
image.png

跟踪分支

从一个远程跟踪分支检出一个本地分支会自动创建一个叫做 “跟踪分支”(有时候也叫做 “上游分支”)。跟 踪分支是与远程分支有直接关系的本地分支。如果在一个跟踪分支上输入 git pull,Git 能自动地识别去哪个 服务器上抓取、合并到哪个分支。
当克隆一个仓库时,它通常会自动地创建一个跟踪 origin/master 的 master 分支。然而,如果你愿意的话 可以设置其他的跟踪分支 - 其他远程仓库上的跟踪分支,或者不跟踪 master 分支。最简单的就是之前看到的例 子,运行 git checkout -b [branch] [remotename]/[branch]。这是一个十分常用的操作所以 Git 提 供了 —track 快捷方式:

  1. $ git checkout --track origin/serverfix
  2. Branch serverfix set up to track remote branch serverfix from origin.
  3. Switched to a new branch 'serverfix'

如果想要将本地分支与远程分支设置为不同名字,你可以轻松地增加一个不同名字的本地分支的上一个命令:

  1. $ git checkout -b sf origin/serverfix
  2. Branch sf set up to track remote branch serverfix from origin.
  3. Switched to a new branch 'sf'


可以使用 -u 或 —set-upstream 选项运行 git branch 来显式地设置已有的本地分支跟踪一个刚刚拉取下来的远程分支:

  1. $ git branch -u origin/serverfix
  2. Branch serverfix set up to track remote branch serverfix from origin.

注:当设置好跟踪分支后,可以通过 @{upstream} 或 @{u} 快捷方式来引用它。所以在 master 分支时并且它正在跟踪 origin/master 时,如果愿意的话可以使用 git merge @{u} 来 取代 git merge origin/master。

如果想要查看设置的所有跟踪分支,可以使用 git branch 的 -vv 选项。这会将所有的本地分支列出来并且包 含更多的信息,如每一个分支正在跟踪哪个远程分支与本地分支是否是领先、落后或是都有。

  1. $ git branch -vv
  2. iss53 7e424c3 [origin/iss53: ahead 2] forgot the brackets
  3. master 1ae2a45 [origin/master] deploying index fix
  4. * serverfix f8674d9 [teamone/server-fix-good: ahead 3, behind 1] this
  5. should do it
  6. testing 5ea463a trying something new

serverfix 分支正在跟踪 teamone 服务器上的 server-fix-good 分支并且领先 2 落后 1,意味着服务器上 有一次提交还没有合并入同时本地有三次提交还没有推送。最后看到 testing 分支并没有跟踪任何远程分支。
注:需要重点注意的一点是这些数字的值来自于你从每个服务器上最后一次抓取的数据。这个命令并没有连接服务 器,它只会告诉你关于本地缓存的服务器数据。如果想要统计最新的领先与落后数字,需要在运行此命令前抓取 所有的远程仓库。可以像这样做:$ git fetch —all; git branch -vv

拉取

🤔 能真的理解fetch不修改工作目录的含义吗?即test和origin/test是不一样的,需要手动pull?
🤔 而如果设置了跟踪分支,则会帮你做这个merge动作?

当 git fetch 命令从服务器上抓取本地没有的数据时,它并不会修改工作目录中的内容。它只会获取数据然后 让你自己合并。然而,有一个命令叫作 git pull 在大多数情况下它的含义是一个 git fetch 紧接着一个 git merge 命令。如果有一个像之前章节中演示的设置好的跟踪分支,不管它是显式地设置还是通过 clone 或 checkout 命令为你创建的,git pull 都会查找当前分支所跟踪的服务器与分支,从服务器上抓取数据然后尝 试合并入那个远程分支。

由于 git pull 的魔法经常令人困惑所以通常单独显式地使用 fetch 与 merge 命令会更好一些。

删除远程分支

  1. $ git push origin --delete serverfix
  2. To https://github.com/schacon/simplegit
  3. - [deleted] serverfix

基本上这个命令做的只是从服务器上移除这个指针。Git 服务器通常会保留数据一段时间直到垃圾回收运行,所 以如果不小心删除掉了,通常是很容易恢复的 😄。

3.6 变基 11p 10:16-10:39

在 Git 中整合来自不同分支的修改主要有两种方法:merge 以及 rebase。在

相比较merge,它会把两个分支的最新快照(C3 和 C4)以及二者最近的 共同祖先(C2)进行三方合并,合并的结果是生成一个新的快照(并提交)。
image.png

rebase则是可以提取在 C4 中引入的补丁和修改,然后在 C3 的基础上再应用一次。在 Git 中,这种 操作就叫做 变基。你可以使用 rebase 命令将提交到某一分支上的所有修改都移至另一分支上,就好像“重新 播放”一样。
image.png

  1. $ git checkout experiment
  2. $ git rebase master
  3. First, rewinding head to replay your work on top of it...
  4. Applying: added staged command

现在回到 master 分支,进行一次快进合并。

  1. $ git checkout master
  2. $ git merge experimen

image.png

本节对于变基可能导致的一些问题,进行了讨论,提供了具体的 解决办法。在自己熟练使用它之前,不深究。

另外关于merge和rebase那种做法更好?

有一种观点认为,仓库的提交历史即是 记录实际发生过什么。它是针对历史的文档,本身就有价值,不能乱 改。从这个角度看来,改变提交历史是一种亵渎,你使用谎言掩盖了实际发生过的事情。如果由合并产生的 提交历史是一团糟怎么办?既然事实就是如此,那么这些痕迹就应该被保留下来,让后人能够查阅。

另一种观点则正好相反,他们认为提交历史是 项目过程中发生的故事。没人会出版一本书的第一批草稿,软件 维护手册也是需要反复修订才能方便使用。持这一观点的人会使用 rebase 及 filter-branch 等工具来编写故事,怎么方便后来的读者就怎么写。

现在,让我们回到之前的问题上来,到底合并还是变基好?希望你能明白,并没有一个简单的答案。Git 是一个 非常强大的工具,它允许你对提交历史做许多事情,但每个团队、每个项目对此的需求并不相同。既然你已经分 别学习了两者的用法,相信你能够根据实际情况作出明智的选择。

列举和寻找答案

文档单词有哪些

  • SYNOPSIS 纲要

    如何阅读git的帮助文档?

    1. $ git help <verb>
    2. $ git <verb> --help
    3. $ man git-<verb>

上面是三种方法都可以在terminal查看文档,使用-w参数可以打开浏览器查看:git help -w log
如果git版本过旧会提示无html,可以直接在网站上搜索 https://git-scm.com/docs/git-log

废弃较早的某个提交?

提交的内容是否被后续提交所修改
如果废弃的内容,没有被后续提交使用:git rebase -i commit-id,commit-id是希望废弃的父节点

思路:git revert,git rebase -i commitid

如何直接同步自己不在的分支上并且直接合并它?

如果你在a分支上,想合并b分支的最新代码,比较麻烦的做法是

  1. git checkout b
  2. git pull
  3. git checkout a
  4. git merge b

一种更简单的的做法是:

  1. git fetch
  2. git merge origin/b

注意:fetch完后,不能直接merge b,因为fetch虽然会更新仓库,但是不会替你移动分支的指针,需要git pull

rebase 操作实践

实践1

假定在main和fix分支出现了分叉,想要将fix代码rebase到main上:

  1. 开发者,先基于本地的main c1切出fix分支
  2. 这时有人在远程仓库往main中提交了c2 (
  3. 开发者这在fix分支修复问题提交了c3

要进行一个顺利的rebase过程

  1. 使用 git branch -vv 查看各分支的落后情况:主要是看本地的main是不是最新的
    1. local main: c1-c3
    2. remote main: c1-c2
  2. 应该显示main behind 1 ahead 1,但是必须 git fetch origin 同步,因为-vv依赖的是过时的信息
  3. 以main的最新代码作为rebase基准,git checkout main,git pull
  4. 然后执行 git checkout fix,git rebase main,fix代码变成了以main为基准的代码(还未推送)
  5. 如果遇到了冲突,需要手动解决冲突后,git add,git rebase —continue 就算是rebase完成了
  6. 注意这时候的分支情况是
    1. c1 - c2 - c3’, fix指向c3’, main指向c2
  7. git checkout main,git merge fix,git push
    1. c1 - c2 - c3’, fix指向c3’, main指向c3’

关于rebase容易蚌埠的地方

  1. 假定你基于某个分支a切除了新分支b,并且改了内容,这时候直接rebase a会提示你是最新代码
  2. 假定你基于某个分支a切除了新分支b,部分同步了a的内容(后续又多个提交,你只在中间某个事件点通不过一次),那么rebase是可以进行的,结果就是未同步的提交丢失掉
  3. 直接看是被rebase的分支是否是最新代码,需要先自行git fetch origin,然后执行git branch -vv
  4. 如果被rebase的分支是最新代码,那么rebase过程遇到冲突,手动解决冲突后,git add,再git rebase —continue 就完成了rebase。

    实践2

    假定个分别三次提交分别为c1, c2, c3,假定要将c3合并到c2中,那么应该执行 rebase -i HEAD~n,请问n应该是多少?
    我们的目的是合并c3到c2,一种写法是 git rebase -i c1-commit-id,因为虽然是要将c1合并,但是c2要出现再编辑选项中,对应的数字就是2(c3 HEAD, c2 HEAD~1, c1 HEAD@~2)。
    另外,如果线上已经是最c3,而我们rebase的版本(领先1版本-多了c4,落后2版本-c2\c3被撤回),这时要生效需要git push 增加 -f 的参数。

实践3

用户1 提交test:a并推送
用户2 拉取代码,提交test:b并推送
用户1 提交test:c
用户1 想把test:c代码跟test:a合并执行rebase -i HEAD~2,对test:c使用fixup
用户1 推送代码,提示远程有人提交代码,于是执行git pull,并解决冲突(如有)再执行git push

sourcemap展示的序列图为

image.png

实践4

注:前4步和实践3一样
用户1 提交main:a并推送
用户2 拉取代码,提交main:b并推送
用户1 提交main:c
用户1 想把main:c代码跟main:a合并执行rebase -i HEAD~2,对main:c使用fixup
用户1 推送代码,提示远程有人提交代码,于是执行git push -f

sourcemap展示的序列图为
image.png

差异点:

  1. 直接push -f会导致remote中丢失掉用户2的改动,用户2拉代码的时候需要处理冲突
  2. 拉取代码后remote不会丢失,用户2拉取代码不确定是否仍旧需要手动处理冲突

真实案例,冲掉了用户2的提交,用户2处理了冲突
image.png
用户1更新之后,将HEAD移动了位置
image.png

如何看懂sourcetree

知识点:HEAD,本地分支,远程分支,落后n个版本,超前n个版本。
下图是进行rebase过程的一个视图,这里尝试解释它的内容

  • main 和 fix 都是本地分支,origin/main 和 origin/fix都是远程分支
  • main 超前1个版本,说明代码还没push到origin
  • fix超前3个版本说明有3个改动还没push到origin,有1个改动没有pull,它是怎么发生的呢?

image.png
简单来说,在一次rebase中,我们在fix分支上rebase main,结果就会在main的后续追加一个fix,fix从而跟origin/fix就脱钩了。这时候通常会切到main,然后merge fix,然后main分支上看起来超前1个。代码在master上提交后,可以删除掉fix分支。也可以仍旧让origin/fix保持独立性,即在fix上git pull,处理好可能的冲突再提交。
image.png

其他问题

🤔 评论区的知识是对课程内容很好的补充。
🤔 将每个命令的理解制作成一个手册,使用时可以快速查询,遇到不理解的在补充进去
🤔 很多命令的用法在help总都有体现,自己不太会看而已,比如git clone就可以自己指定新名字