将IPython笔记本电脑置于版本控制之下的好策略是什么?
笔记本格式非常适合版本控制:如果想要版本控制笔记本和输出,那么这非常有效 . 当人们只想对输入进行版本控制时,就会产生烦恼,排除可能是大型二进制blob的单元输出(也就是“构建产品”),特别是对于电影和情节 . 特别是,我试图找到一个良好的工作流程:
-
允许我在包含或排除输出之间进行选择,
如果我不想要它, -
会阻止我意外提交输出,
-
允许我保持本地版本的输出,
-
允许我查看当我使用我的版本控制系统更改输入时(即如果我只对版本控制输入但我的本地文件有输出,那么我希望能够看到输入是否已更改(需要使用版本控制状态命令将始终注册差异,因为本地文件有输出 . )
-
允许我从更新的干净笔记本更新我的工作笔记本(包含输出) . (update)
如上所述,如果我选择包含输出(例如,在使用nbviewer时这是可取的),那么一切都很好 . 问题是当我不想版本控制输出时 . 有一些工具和脚本可以剥离笔记本的输出,但我经常会遇到以下问题:
-
我意外地提交了一个带有输出的版本,从而污染了我的存储库 .
-
我清除输出以使用版本控制,但实际上宁愿将输出保留在我的本地副本中(例如,有时需要一段时间才能重现) .
-
一些剥离输出的脚本与
Cell/All Output/Clear
菜单选项相比稍微改变了格式,从而在差异中产生了不必要的噪声 . 这可以通过一些答案来解决 . -
当将更改提取到文件的干净版本时,我需要找到一些方法将这些更改合并到我的工作笔记本中,而无需重新运行所有内容 . (update)
我已经考虑过几个选项,我将在下面讨论,但还没有找到一个很好的综合解决方案 . 完整的解决方案可能需要对IPython进行一些更改,或者可能依赖于一些简单的外部脚本 . 我目前使用mercurial,但想要一个也适用于git的解决方案:理想的解决方案是与版本控制无关 .
这个问题已经多次讨论过,但从用户的角度来看,没有明确或明确的解决方案 . 这个问题的答案应该提供明确的策略 . 如果它需要最近的(甚至开发)版本的IPython或一个易于安装的扩展,那就没问题了 .
Update: 我一直在玩my modified notebook版本,可选择使用Gregory Crosswhite's suggestions保存每个保存的 .clean
版本 . 这满足了我的大多数约束,但是仍然没有解决以下问题:
-
这还不是一个标准的解决方案(需要修改ipython源 . 有没有办法通过简单的扩展来实现这种行为?需要某种on-save钩子 .
-
我对当前工作流程的一个问题是拉动变化 . 这些将进入
.clean
文件,然后需要以某种方式集成到我的工作版本中 . (当然,我总是可以重新执行笔记本,但这可能会很痛苦,特别是如果某些结果取决于长时间的计算,并行计算等) . 我还不知道如何解决这个问题 . 也许涉及像ipycache这样的扩展的工作流可能会起作用,但这似乎有点过于复杂 .
注意事项
删除(剥离)输出
-
当笔记本电脑运行时,可以使用
Cell/All Output/Clear
菜单选项删除输出 . -
有一些用于删除输出的脚本,例如删除输出的脚本nbstripout.py,但不会产生与使用笔记本界面相同的输出 . 这最终包含在ipython/nbconvert repo中,但是已经关闭,说明这些更改现在包含在ipython/ipython中,但相应的功能似乎尚未包括在内 . (update) 话虽如此,Gregory Crosswhite's solution表明这很容易做到,即使没有调用ipython/nbconvert,所以如果可以正确地挂钩,这种方法可能是可行的 . (但是,将它附加到每个版本控制系统,似乎不是好主意 - 这应该以某种方式挂钩到笔记本机制 . )
新闻组
问题
-
1280: Clear-all on save option (Open) . (关注this discussion . )
-
3295: autoexported notebooks: only export explicitly marked cells (Closed) . 由扩展11 Add writeandexecute magic (Merged)解决 .
拉请求
-
1621: clear In[] prompt numbers on "Clear All Output" (Merged) . (另见2519 (Merged) . )
-
3291: Add the option to skip output cells when saving. (Closed) . 这似乎非常相关,但是关闭了使用"clean/smudge"过滤器的建议 . 相关问题what can you use if you want to strip off output before running git diff?似乎没有得到回答 .
-
3747: ipynb -> ipynb transformer (Closed) . 这是4175的重新定位 .
17 回答
我已经构建了python包来解决这个问题
https://github.com/brookisme/gitnb
它提供了一个带有git启发语法的CLI,用于在git仓库中跟踪/更新/区分笔记本 .
这是一个例子
请注意,我正在使用“gitnb commit”的最后一步是提交到你的git repo . 它本质上是一个包装
还有几种方法,并且可以进行配置,以便在每个阶段需要或多或少的用户输入,但这是一般的想法 .
在下面的帖子中讨论的想法如何,应该保留笔记本的输出,并且可能需要很长时间来生成它,并且它很方便,因为GitHub现在可以渲染笔记本 . 为导出.py文件添加了自动保存挂钩,用于差异和.html与不使用笔记本或git的团队成员共享 .
https://towardsdatascience.com/version-control-for-jupyter-notebook-3e6cef13392d
(2017-02)
strategies
on_commit():
剥离输出> name.ipynb(
nbstripout
,)剥离输出> name.clean.ipynb(
nbstripout
,)总是
nbconvert
到python:name.ipynb.py(nbconvert
)始终转换为markdown:name.ipynb.md(
nbconvert
,ipymd
)vcs.configure():
git difftool,mergetool:来自nbdime的nbdiff和nbmerge
tools
nbstripout
:从笔记本中剥离输出src:https://gist.github.com/minrk/6176788
src:https://github.com/kynan/nbstripout
pip install nbstripout; nbstripout install
ipynb_output_filter
:从笔记本中剥离输出src:https://github.com/toobaz/ipynb_output_filter/blob/master/ipynb_output_filter.py
ipymd
:{Jupyter,Markdown,O'Reilly Atlas Markdown,OpenDocument,.py}之间的转换src:https://github.com/rossant/ipymd
nbdime :"Tools for diffing and merging of Jupyter notebooks."(2015)
src:https://github.com/jupyter/nbdime
docs:http://nbdime.readthedocs.io/
nbdiff
:以对终端友好的方式比较笔记本电脑nbdime nbdiff works as a git diff tool :https://nbdime.readthedocs.io/en/latest/#git-integration-quickstart
nbmerge
:具有自动冲突解决方案的笔记本电脑的三向合并nbdime nbmerge works as a git merge tool
nbdiff-web
:向您展示笔记本电脑的丰富渲染差异nbmerge-web
:为笔记本电脑提供基于Web的三向合并工具nbshow
:以终端友好的方式呈现单个笔记本要跟进Pietro Battiston的优秀脚本,如果你得到这样的Unicode解析错误:
您可以在脚本的开头添加:
好的,所以看起来当前最好的解决方案,根据讨论here,是使git过滤器在提交时自动剥离ipynb文件的输出 .
以下是我为使其工作所做的工作(从该讨论中复制):
我修改了cfriedline 's nbstripout file slightly to give an informative error when you can' t导入最新的IPython:https://github.com/petered/plato/blob/fb2f4e252f50c79768920d0e47b870a8d799e92b/notebooks/config/strip_notebook_output并将其添加到我的仓库中,让我们说
./relative/path/to/strip_notebook_output
还将文件.gitattributes文件添加到repo的根目录,其中包含:
并创建了一个
setup_git_filters.sh
包含然后跑了
source setup_git_filters.sh
. 花哨的$(git rev-parse ...)就是在任何(Unix)机器上找到你的repo的本地路径 .这是来自Cyrille Rossant的IPython 3.0的新解决方案,它坚持使用markdown文件而不是基于json的ipymd文件:
https://github.com/rossant/ipymd
只是遇到“jupytext”,看起来像一个完美的解决方案 . 它从笔记本生成一个.py文件,然后保持同步 . 您可以通过.py文件对版本控制,差异和合并输入进行版本控制,而不会丢失输出 . 当您打开笔记本时,它使用.py作为输入单元格,使用.ipynb作为输出 . 如果你想在git中包含输出,那么你可以添加ipynb .
https://github.com/mwouts/jupytext
不幸的是,我对Mercurial了解不多,但是我可以给你一个可以与Git一起使用的解决方案,希望你能够将我的Git命令翻译成他们的Mercurial等价物 .
对于后台,在Git中,
add
命令将对文件所做的更改存储到暂存区域 . 完成此操作后,Git将忽略对该文件的任何后续更改,除非您告诉它也将其暂存 . 因此,以下脚本,对于每个给定文件,剥离所有outputs
和prompt_number sections
,对剥离文件进行分阶段,然后恢复原始文件:NOTE: 如果运行此命令会收到类似
ImportError: No module named IPython.nbformat
的错误消息,则使用ipython
来运行脚本而不是python
.一旦在要提交其更改的文件上运行脚本,只需运行
git commit
.正如所指出的,
--script
在3.x
中已弃用 . 可以通过应用post-save-hook来使用此方法 . 特别是,将以下内容添加到ipython_notebook_config.py
:代码取自#8009 .
我创建了nbstripout,基于MinRKs gist,它支持Git和Mercurial(感谢mforbes) . 它既可以在命令行中单独使用,也可以作为过滤器使用,可以通过
nbstripout install
/nbstripout uninstall
轻松(非)安装在当前存储库中 .从PyPI或简单地获取它
我们有一个合作项目,产品是Jupyter笔记本电脑,我们在过去的六个月中使用了一种方法很好:我们激活自动保存
.py
文件并跟踪.ipynb
文件和.py
文件 .这样,如果有人想查看/下载最新的笔记本,他们可以通过github或nbviewer做到这一点,如果有人想看看笔记本代码是如何变化的,他们可以只查看对
.py
文件的更改 .For Jupyter notebook servers ,这可以通过添加行来完成
到
jupyter_notebook_config.py
文件并重新启动笔记本服务器 .如果您不确定在哪个目录中找到
jupyter_notebook_config.py
文件,可以键入jupyter --config-dir
,如果在那里找不到该文件,可以通过键入jupyter notebook --generate-config
来创建它 .For Ipython 3 notebook servers ,这可以通过添加行来完成
到
ipython_notebook_config.py
文件并重新启动笔记本服务器 . 这些行来自github问题答案@minrk provided和@dror也包含在他的SO答案中 .For Ipython 2 notebook servers ,这可以通过使用以下命令启动服务器来完成:
或者添加该行
到
ipython_notebook_config.py
文件并重新启动笔记本服务器 .如果您不确定在哪个目录中找到
ipython_notebook_config.py
文件,可以键入ipython locate profile default
,如果在那里找不到该文件,可以通过键入ipython profile create
来创建它 .这是our project on github that is using this approach:这是一个github example of exploring recent changes to a notebook .
我们对此非常满意 .
I finally found a productive and simple way to make Jupyter and Git play nicely together. 我还在迈出第一步,但我已经认为它比其他所有复杂的解决方案都要好得多 .
Visual Studio Code是Microsoft的酷炫开源代码编辑器 . 它有一个很好的Python扩展,现在允许你import a Jupyter Notebook作为python代码 .
将笔记本导入python文件后,所有代码和markdown将一起放在普通的python文件中,注释中带有特殊标记 . 您可以在下图中看到:
你的python文件只包含笔记本输入单元格的内容 . 输出将在拆分窗口中生成 . 你在笔记本中有纯粹的代码,当你执行它时它不会改变 . 没有与您的代码混合输出 . 没有奇怪的Json难以理解的格式来分析你的差异 .
只需纯Python代码,您可以轻松识别每个差异 .
我甚至不需要再编辑我的
.ipynb
文件了 . 我可以在.gitignore
中添加*.ipynb
行 .需要生成笔记本才能发布或与他人分享?没问题,只是在交互式python窗口中click the export button
我一直在使用它只有一天,但最后我可以愉快地使用Jupyter与Git .
P.S . :VSCode代码完成比Jupyter好很多 .
这是我用git的解决方案 . 它允许你像往常一样添加和提交(和差异):这些操作不会改变你的工作树,同时(重新)运行笔记本不会改变你的git历史 .
虽然这可能适用于其他VCS,但我知道它不能满足您的要求(至少VSC不可知) . 尽管如此,它对我来说还是完美的,虽然没有什么特别精彩,很多人可能已经使用过它,但我没有找到关于如何通过Google搜索来实现它的明确指示 . 所以它可能对其他人有用 .
在某处保存this content的文件(对于以下内容,我们假设
~/bin/ipynb_output_filter.py
)使其可执行(
chmod +x ~/bin/ipynb_output_filter.py
)使用以下内容创建文件
~/.gitattributes
完成!
Limitations:
它只适用于git
在git中
,如果你在分支
somebranch
并且你做git checkout otherbranch; git checkout somebranch
,你通常希望工作树不变 . 相反,您将丢失两个分支之间源不同的笔记本的输出和单元格编号 .__一般来说,输出没有版本化,就像Gregory的解决方案一样 . 为了不是每次你做任何涉及结账的事情都把它扔掉,可以通过将它存储在单独的文件中来改变方法(但请注意,在运行上面的代码时,提交id是未知的!),并且可能对它们进行版本控制(但请注意,这需要的不仅仅是
git commit notebook_file.ipynb
,尽管它至少可以保持git diff notebook_file.ipynb
免受base64垃圾的影响) .表示,顺便说一句,如果你确实拉代码(即由不使用此方法的其他人提交)包含一些输出,则输出会正常检出 . 只丢失本地产生的输出 .
我的解决方案反映了这样一个事实,即我个人不喜欢将生成的内容保留为版本 - 请注意,涉及输出的合并几乎可以保证输出或 生产环境 力无效或两者兼而有之 .
EDIT:
** . ipynb filter =
作为内容 . 显然,以相同的方式可以执行相反的操作:仅针对特定存储库启用过滤 .
代码现在保存在自己的git repo中
如果上述说明导致ImportErrors,请尝试在脚本路径前添加“ipython”:
EDIT :2016年5月(2017年2月更新):我的脚本有几种替代方案 - 为了完整性,这里列出了我所知道的:nbstripout(other variants),nbstrip,jq .
这个jupyter扩展使用户可以将jupyter笔记本直接推送到github .
请看这里
https://github.com/sat28/githubcommit
我做了Albert&Rich做的事情 - 不要版本.ipynb文件(因为这些文件可能包含混乱的图像) . 相反,要么始终运行
ipython notebook --script
,要么将c.FileNotebookManager.save_script = True
放在配置文件中,以便在保存笔记本时始终创建(可版本化的).py
文件 .要重新生成笔记本(在签出仓库或切换分支后),我将脚本py_file_to_notebooks.py放在我存储笔记本的目录中 .
现在,在签出一个repo之后,只需运行
python py_file_to_notebooks.py
来生成ipynb文件 . 切换分支后,您可能必须运行python py_file_to_notebooks.py -ov
来覆盖现有的ipynb文件 .为了安全起见,最好还将
*.ipynb
添加到.gitignore
文件中 .编辑:我不再这样做了,因为(A)每次检查分支时都必须从py文件中重新生成笔记本,并且(B)还有其他东西,例如笔记本中的降价丢失 . 我改为使用git过滤器从笔记本中删除输出 . 关于如何做到这一点的讨论是here .
在挖掘之后,我终于找到了this relatively simple pre-save hook on the Jupyter docs . 它剥离单元输出数据 . 您必须将其粘贴到
jupyter_notebook_config.py
文件中(有关说明,请参阅下文) .来自Rich Signell's answer:
我用一种非常务实的方法;它适用于几个笔记本电脑,在几个方面 . 它甚至可以让我周围的笔记本电脑 . 它适用于Windows作为Unix / MacOS .
Al认为很简单,就是解决上面的问题......
概念
基本上, not 跟踪
.ipnyb
-files,只跟踪相应的.py
-files .通过使用
--script
选项启动笔记本电脑 - 服务器,保存笔记本时会自动创建/保存该文件 .那些
.py
-files确实包含所有输入;非代码保存到注释中,单元格边框也是如此 . 可以将这些文件读取/导入(并拖动)到笔记本服务器中以(重新)创建笔记本 . 只有输出消失了;直到重新运行 .我个人使用mercurial来版本跟踪
.py
文件;并使用普通(命令行)命令添加,签入(ect) . 大多数其他(D)VCS将允许这样做 .现在很容易跟踪历史;
.py
是小的,文本的和简单的差异 . 有一段时间,我们需要一个克隆(只是分支;在那里启动第二个笔记本 - 服务器),或者旧版本(检出并导入到笔记本服务器中)等 .提示与技巧
将* .ipynb添加到' .hgignore ',因此Mercurial知道它可以忽略这些文件
创建一个(bash)脚本来启动服务器(使用
--script
选项)并对其进行版本跟踪保存笔记本会保存
.py
文件,但不会将其签入 .这是一个 drawback :人们可以忘记这一点
这也是一个 feature :可以保存笔记本(并在以后继续)而无需集群存储库历史记录 .
祝福
在笔记本电脑仪表板上有一个用于登记/添加/等的按钮会很不错
结账(例如)
file@date+rev.py
)应该会有所帮助 . 添加它会有很多工作要做;也许我会这样做一次 . 到现在为止,我只是手工完成 .