跟踪工作的变化以实现可重复性和协作。
Intuition
无论是单独工作还是与团队一起工作,重要的是有一个系统来跟踪项目的变化,这样就可以恢复到以前的版本,这样其他人就可以重现transformers工作并为之做出贡献。Git是一个分布式版本控制系统,可以让做到这一点。Git 在transformers计算机上本地运行,它会跟踪transformers文件及其历史记录。为了与他人协作,可以使用远程主机(GitHub、GitLab、BitBucket等)来托管transformers文件及其历史记录。将使用 git 将transformers本地更改推送到远程主机或从远程主机拉取其他人的更改。
Git 传统上用于存储小于 100MB 的小文件(脚本、自述文件等)并对其进行版本控制,但是,仍然可以使用指向 blob 存储的文本指针对大型工件(数据集、模型权重等)进行版本控制。这些指针将包含assert所在位置、特定内容/版本(例如通过哈希)等信息。将在版本控制课程中看到这一点,将创建一个指向特定版本的指针transformers数据集和模型。
设置
初始化git
初始化一个本地存储库(.git
目录)来跟踪transformers文件:
git init
Initialized empty Git repository in /Users/goku/Documents/madewithml/MLOps/.git/
可以看到哪些文件未被跟踪或尚未提交:
git status
On branch main
No commits yet
Untracked files:
(use "git add ..." to include in what will be committed)
.flake8
.vscode/
Makefile
...
.gitignore
可以看到有一些不想推送到远程主机的文件,比如transformers虚拟环境、日志、大数据文件等。可以创建一个.gitignore
文件来确保不签入这些文件。
touch .gitignore
将以下文件添加到文件中:
# Data
logs/
stores/
data/
# Packaging
venv/
*.egg-info/
__pycache__/
# Misc
.DS_Store
现在,也将添加data
到transformers.gitignore
文件中,但这意味着其他人在从transformers远程主机上拉取时将无法生成相同的数据assert。为了解决这个问题,将在transformers版本控制课程中推送指向数据文件的指针,以便数据也可以完全按照在本地拥有的方式进行复制。
Tip
查看项目的.gitignore以获得更完整的示例,其中还包括许多通常不想推送到远程存储库的其他系统工件。transformers完整
.gitignore
文件基于 GitHub 的Python 模板,使用的是Mac,因此也添加了相关的全局文件名。
如果git status
现在运行,不应该再看到在文件中定义的.gitignore
文件。
Add to stage
接下来,将transformers工作从工作目录添加到暂存区。
-
可以一次添加一个文件:
git add <filename>
-
可以一次添加所有文件:
git add .
现在运行git status
将向展示所有暂存文件:
git status
On branch main
No commits yet
Changes to be committed:
(use "git rm --cached ..." to unstage)
new file: .flake8
new file: .gitignore
new file: Makefile
...
承诺回购
现在准备将暂存区中的文件提交到本地存储库。默认分支(项目的一个版本)将被称为main
.
git commit -m "added project files"
[main (root-commit) 704d99c] added project files
47 files changed, 50651 insertions(+)
create mode 100644 .flake8
create mode 100644 .gitignore
create mode 100644 Makefile
...
提交需要一条消息,指示发生了哪些更改。如果需要,可以git commit --amend
用来编辑提交消息。如果进行git status
检查,会发现暂存区没有其他内容可以提交。
git status
On branch main
nothing to commit, working tree clean
推送到远程
现在已准备好将更新从本地存储库推送到远程存储库。首先在GitHub (或任何其他远程存储库)上创建一个帐户,然后按照说明创建一个远程存储库(它可以是私有的或公共的)。在transformers本地存储库中,将设置transformers用户名和电子邮件凭据,以便可以将更改从本地存储库推送到远程存储库。
# Set credentials via terminal
git config --global user.name <USERNAME>
git config --global user.email <EMAIL>
可以像这样快速验证是否设置了正确的凭据:
# Check credentials
git config --global user.name
git config --global user.email
接下来,需要在本地和远程存储库之间建立连接:
# Push to remote
git remote add origin https://github.com/<USERNAME>/<REPOSITORY_NAME>.git
git push -u origin main # pushing the contents of our local repo to the remote repo
# origin signifies the remote repository
Developing
现在准备好开始添加到transformers项目并提交更改。
cloning
如果(或其他人)还没有设置本地存储库并连接到远程主机,可以使用cloning命令:
git clone <REMOTE_REPO_URL> <PATH_TO_PROJECT_DIR>
也可以cloning存储库的特定分支:
git clone -b <BRANCH> <REMOTE_REPO_URL> <PATH_TO_PROJECT_DIR>
<REMOTE_REPO_URL>
是远程仓库的位置(例如 https://github.com/GokuMohandas/mlops-course)。<PATH_TO_PROJECT_DIR>
是要将项目cloning到的本地目录的名称。
创建分支
当想要添加或更改某些内容时,例如添加功能、修复错误等,在开发之前创建一个单独的分支始终是最佳实践。这在与团队合作时尤为重要,这样就可以在讨论和审查后将transformers工作干净利落地合并到main
分支机构中。
将从创建一个新分支开始:
git checkout -b <NEW_BRANCH_NAME>
可以使用以下命令查看创建的所有分支,其中 * 表示当前的分支:
git branch
* convnet
main
可以使用以下方法轻松地在现有分支之间切换:
git checkout <BRANCH_NAME>
进入分支后,可以对项目进行更改并提交这些更改。
git add .
git commit -m "update model to a convnet"
git push origin convnet
请注意,正在将此分支推送到transformers远程存储库,该存储库尚不存在,因此 GitHub 将相应地创建它。
拉取请求(PR)
当将新分支推送到远程存储库时,需要创建一个拉取请求 (PR) 以与另一个分支(例如main
本例中的分支)合并。将transformers工作与另一个分支(例如 main)合并时,它被称为拉取请求,因为请求该分支拉取提交的工作。可以使用此处概述的步骤创建拉取请求:创建拉取请求。
note
可以使用 git CLI 命令合并分支和解决冲突,但最好使用在线界面,因为可以轻松地将更改可视化,与队友进行讨论等。
# Merge via CLI git push origin convnet git checkout main git merge convnet git push origin main
Pull
一旦接受了拉取请求,transformersmain
分支现在就更新了transformers更改。然而,更新只发生在远程存储库上,所以也应该将这些更改拉到transformers本地main
分支。
git checkout main
git pull origin main
Delete branches
一旦完成了一个分支的工作,就可以删除它以防止transformers存储库混乱。可以使用以下命令轻松删除分支的本地和远程版本:
# Delete branches
git branch -d <BRANCH_NAME> # local
git push origin --delete <BRANCH_NAME> # remote
合作
到目前为止,集成迭代开发的工作流程非常顺利,但在协作环境中,可能需要解决冲突。假设有两个分支(a
和b
)是从main
分支创建的。这是要尝试模拟的内容:
- 开发人员 A 和 B 分叉
main
分支进行一些更改 - 开发人员 A 进行更改并向
main
分支提交 PR。 - 开发者 B 对与开发者 A 相同的行进行更改,并向 提交 PR
main
。 - 现在有合并冲突,因为两个开发人员都更改了同一行。
- 选择要保留的代码版本并解决冲突。
main
当尝试合并第二个 PR 时,必须解决这个新 PR 与分支中已经存在的冲突。
main
可以通过选择保留哪些内容(与分支合并的当前内容a
或本b
分支)并删除另一个内容来解决冲突。然后可以成功合并 PR 并更新本地的main
分支。
<<<<<< BRANCH_A
<CHANGES FROM BRANCH A>
======
<CHANGES FROM BRANCH B>
>>>>>> BRANCH_B
一旦冲突得到解决并且合并了 PR,就可以更新transformers本地存储库以反映这些决定。
git checkout main
git pull origin main
note
只是有冲突,因为这两个分支都是从该
main
分支的先前版本派生出来的,并且它们碰巧都更改了相同的内容。如果先创建一个分支,然后在创建第二个分支之前更新主分支,就不会有任何冲突。但在协作环境中,不同的开发人员可能随时分叉出相同版本的分支。
检查
Git 允许在许多不同的层次上检查工作的当前和以前的状态。让探索最常用的命令。
Status
已经多次使用 status 命令,因为它对于快速查看工作树的状态非常有用。
# Status
git status
git status -s # short format
日志
如果想查看所有提交的日志,可以使用 log 命令。也可以通过在 Git 在线界面上检查特定的分支历史来做同样的事情。
# Log
git log
git log --oneline # short version
704d99c (HEAD -> main) added project files
提交 ID 的长度为 40 个字符,但可以用前几个字符来表示它们(Git SHA 的默认值是七位数字)。如果有歧义,Git 会通知,可以简单地添加更多的提交 ID。
差异
如果想知道两次提交之间的区别,可以使用 diff 命令。
# Diff
git diff # all changes between current working tree and previous commit
git diff <COMMIT_A> <COMMIT_B> # diff b/w two commits
git diff <COMMIT_A>:<PATH_TO_FILE> <COMMIT_B>:<PATH_TO_FILE> # file diff b/w two commits
diff --git a/.gitignore b/.gitignore
index 288973d..028aa13 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,7 +1,6 @@
# Data
logs/
stores/
-data/
Blame
最有用的检查命令之一是 blame,它允许查看文件中每一行的提交。
# Blame
git blame <PATH_TO_FILE>
git blame -L 1,3 <PATH_TO_FILE> # blame for lines 1 and 3
Time travel
有时可能做了一些希望可以改变的事情。在生活中并不总是可以做到这一点,但在 Git 的世界里,它是!
恢复
有时可能只想撤消添加或暂存文件,可以使用restore命令轻松完成。
# Restore
git restore -- <PATH_TO_FILE> <PATH_TO_FILE> # will undo any changes
git restore --stage <PATH_TO_FILE> # will remove from stage (won't undo changes)
重置
现在,如果已经提交但还没有推送到远程,可以通过将分支指针移动到该提交来重置到之前的提交。请注意,这将撤消自上次提交以来所做的所有更改。
# Reset
git reset <PREVIOUS_COMMIT_ID> # or HEAD^
HEAD
是引用上一次提交的快速方法。两者HEAD
和任何先前的提交 ID 都可以带有一个^
或~
符号作为相对引用。^
n 指的是提交的第 n 个父级,而~
n指的是第n个祖父母。当然,总是可以明确地使用提交 ID,但是这些短手可以派上用场,无需git log
检索提交 ID 即可进行快速检查。
恢复
但是可以通过添加一个新的提交来恢复某些以前的提交来继续前进,而不是将分支指针移动到之前的提交。
# Revert
git revert <COMMIT_ID> ... # rollback specific commits
git revert <COMMIT_TO_ROLLBACK_TO>..<COMMIT_TO_ROLLBACK_FROM> # range
Checkout
有时可能想暂时切换回以前的提交,只是为了探索或提交一些更改。最好在单独的分支中执行此操作,如果想保存更改,需要创建一个单独的 PR。请注意,如果您确实检查了以前的提交并提交了 PR,则可以覆盖两者之间的提交。
# Checkout
git checkout -b <BRANCH_NAME> <COMMIT_ID>
最佳实践
有很多不同的工作可以与 git 一起工作,有时当其他开发人员遵循不同的做法时,它会很快变得不守规矩。在处理提交和分支时,这里有一些被广泛接受的最佳实践。
提交
-
经常提交,这样每个提交都有一个明确的关联更改,您可以批准/回滚。
-
如果在推送到远程主机之前有多个提交,请尝试压缩提交。
-
避免整体提交(即使您经常存储和变基),因为它会导致许多组件崩溃并造成代码审查的噩梦。
-
将有意义的消息附加到提交中,以便开发人员确切地知道 PR 需要什么。
-
使用标签来表示有意义且稳定的应用程序版本。
# Tags git tag -a v0.1 -m "initial release"
-
不要删除提交历史(重置),而是使用revert回滚并提供推理。
分支机构
- 在处理功能、错误等时创建分支,因为这使得添加和恢复到
main
分支变得非常容易。 - 避免使用神秘的分支名称。
- 将您的分支维护
main
为始终有效的“演示就绪”分支。 - 用规则保护分支机构(尤其是
main
分支机构)。
标签
利用 git标签标记重要的发布提交。可以通过终端或在线远程界面创建标签,这也可以对以前的提交完成(以防忘记)。
# Tags
git tag # view all existing tags
git tag -a <TAG_NAME> -m "SGD" # create a tag
git checkout -b <BRANCH_NAME> <TAG_NAME> # checkout a specific tag
git tag -d <TAG_NAME> # delete local tag
git push origin --delete <TAG_NAME> # delete remote tag
git fetch --all --tags # fetch all tags from remote
标签名称通常遵循版本命名约定,例如
v1.4.2
数字从左到右表示主要、次要和错误更改。
更多干货,第一时间更新在以下微信公众号:
您的一点点支持,是我后续更多的创造和贡献
转载到请包括本文地址 更详细的转载事宜请参考文章如何转载/引用
本文主体源自以下链接:
@article{madewithml,
author = {Goku Mohandas},
title = { Made With ML },
howpublished = {\url{https://madewithml.com/}},
year = {2022}
}