2 初始化仓库¶
约 1280 个字 65 行代码 预计阅读时间 5 分钟
Abstract
在这部分中,我们会实现 xgit init 指令,它会初始化一个空的仓库。
在这章,我们学习如何实现一个命令。
这是一个非常简单的部分!我们创建一个 .git 仓库,然后在其中创建一些文件和目录。我们暂时不讨论这些文件和目录的作用,只是简单地创建它们。
2.1 效果¶
我们可以通过 --help 来查看 xgit init 的使用方法:
Usage: xgit [OPTIONS] COMMAND [ARGS]...
╭─ Options ─────────────────────────────────────────────────────────────────────╮
│ --help Show this message and exit. │
╰───────────────────────────────────────────────────────────────────────────────╯
╭─ Commands ────────────────────────────────────────────────────────────────────╮
│ commit │
│ init 初始化一个 git 仓库。 │
╰───────────────────────────────────────────────────────────────────────────────╯
xgit init --help
Usage: xgit init [OPTIONS] [DIRECTORY]
初始化一个 git 仓库。
╭─ Arguments ───────────────────────────────────────────────────────────────────╮
│ directory [DIRECTORY] 要初始化 git 仓库的目录 [default: .] │
╰───────────────────────────────────────────────────────────────────────────────╯
╭─ Options ─────────────────────────────────────────────────────────────────────╮
│ --help Show this message and exit. │
╰───────────────────────────────────────────────────────────────────────────────╯
可以看到 xgit init 会在指定目录下初始化一个 git 仓库;如果没有指定,则默认在当前路径下新建。
作为一个示例,我们在一个空目录下运行 xgit init .,可以看到它创建了 .git 目录,并创建了一些文件和目录(这里 HEAD 是文件,其他的都是目录):
└── .git
├── HEAD
├── objects
└── refs
├── heads
└── tags
6 directories, 1 file
我们会在后面讨论这些文件和目录的作用。这次我们的目标就是把它们创建出来!
2.2 预备知识¶
.git 目录包含 git 完成版本控制所需要的 全部 信息。即使目录中的其它文件全部被删除,我们也可以通过 .git 目录中的信息来恢复它们。我们在后面的章节中再逐步了解这些信息。
在这节中,我们希望创建一个命令 xgit init,它接受一个可选的参数作为创建 git 仓库的目录;即如果没有指定目录,则默认在当前目录下创建。
在 typer 中,通过给实现命令的函数添加参数,就能在生成出的命令中接收对应的参数;而如果这个参数有默认值,那么它就是可选的。
2.3 我的实现¶
您可以在 这个 commit 中看到我的实现。
我们增加了一些文件和目录:
% tree . --gitignore
.
├── readme.md
├── scripts
│ ├── lint.sh
│ └── lint_all.sh
├── setup.py
└── xgit
├── __init__.py
├── cli.py
+ ├── commands
+ │ ├── __init__.py
+ │ └── init.py
+ └── constants.py
我们不妨先来看看 cli.py 中的改动:
diff --git a/xgit/cli.py b/xgit/cli.py
index 88ba1d6..cc30cab 100644
--- a/xgit/cli.py
+++ b/xgit/cli.py
@@ -1,14 +1,16 @@
import typer
+from xgit.commands import init
app = typer.Typer(add_completion=False)
-@app.command()
-def init():
- print("Hello World")
+app.command()(init.init)
@app.command()
def commit():
print("Hello World")
def main():
app()
我们从 xgit.commands 中导入了 init,并将 init.init 注册为 app 的一个命令。这样,我们就可以通过 xgit init 来调用 init.init 了。
commands 是我们创建的一个新文件夹,用于存储命令的源码。我们在其中创建了一个空的 __init__.py,这使得我们能够将 commands 视为一个包,因此能够在 cli.py 中使用 from xgit.commands import init。
我们用 app.command()(init.init) 来注册 init.init,这是注册命令的另一种方式。我们仍然保留了 dummy 的 commit 命令,因为当只有一个命令时,typer 的行为会有一些不同;如果感兴趣那样会发生什么的话您可以自己试一试。
我们来看看 init.py 的内容:
| xgit/commands/init.py | |
|---|---|
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 | |
我们可以看到,我们使用了 typer.Argument 来定义一个参数,它会被 typer 自动解析。我们使用了 typing_extensions.Annotated 来为这个参数添加了一个 help,这样我们就可以通过 --help 来查看这个参数的帮助信息了。
类型注解与 Annotated
我们知道,Python 是一个动态类型语言,这意味着我们不需要在定义变量时指定它的类型。但是,为了让 IDE 和静态分析工具就能够更好地理解代码,从而提供更好的提示和检查,Python 提供了类型注解的功能。
typing_extensions 中的 Annotated 允许开发者在提供类型注解的同时提供一些额外信息。Annotated 主要是给库使用的,它的第一个参数表示这个变量的类型,而后面的参数表示额外的信息。
命令的参数
这里的 init 被注册为一个 command,因此它的参数 directory 会被理解为 command 的一个参数 (argument);即使我们不写类型注解也是如此。
但是,我们通过 name: Annotated[str, typer.Argument()] 的方式明确说明它是一个参数,因为这样我们可以给 typer 提供更多信息,例如这里例子中的 help。
另外,由于我们给这个参数了一个默认值 = ".",因此它成为了一个可选参数。在 --help 中,我们看到 Usage: xgit init [OPTIONS] [DIRECTORY],这里 DIRECTORY 被 [] 包裹,说明它是一个可选的参数。
xgit.constants 是我们新建的 xgit/constants.py,它会用来存放一些常量,目前只有 git 目录的名字:
GIT_DIR = ".git"
Path 是 pathlib 提供的一个类,它包装了关于路径、文件、目录的一些操作。/ 运算符会将两个 Path 连接起来,因此 directory / GIT_DIR 会得到一个新的 Path,它表示 directory 中的 .git 目录。
我们创建了 .git ,并在其中创建了一些文件和目录。在下一章开始,我们会用到这些文件和目录。
您可以在 2.1 效果 中回顾我们这一章的效果。