在版本控制下使用 IPython / Jupyter Notebooks
Posted
技术标签:
【中文标题】在版本控制下使用 IPython / Jupyter Notebooks【英文标题】:Using IPython / Jupyter Notebooks Under Version Control 【发布时间】:2013-09-15 02:19:37 【问题描述】:让IPython 笔记本处于版本控制之下的好策略是什么?
notebook 格式非常适合版本控制:如果想要对 notebook 和输出进行版本控制,那么这很有效。当一个人只想对输入进行版本控制时,麻烦就来了,不包括可能是大二进制 blob 的单元输出(又名“构建产品”),尤其是对于电影和情节。特别是,我正在努力寻找一个好的工作流程:
允许我在包含或排除输出之间进行选择, 防止我在不需要时意外提交输出, 允许我在本地版本中保留输出, 允许我使用我的版本控制系统查看输入何时发生更改(即,如果我只对输入进行版本控制但我的本地文件有输出,那么我希望能够查看输入是否已更改(需要提交)。使用版本控制状态命令将始终记录差异,因为本地文件有输出。) 允许我从更新的干净笔记本更新我的工作笔记本(包含输出)。 (更新)如前所述,如果我选择包含输出(例如,在使用 nbviewer 时这是可取的),那么一切都很好。问题是当我不想想要对输出进行版本控制时。有一些工具和脚本可以剥离笔记本的输出,但我经常遇到以下问题:
-
我不小心提交了带有输出的版本,从而污染了我的存储库。
我清除输出以使用版本控制,但实际上更愿意将输出保留在我的本地副本中(例如,有时需要一段时间才能重现)。
与
Cell/All Output/Clear
菜单选项相比,一些去除输出的脚本会稍微改变格式,从而在差异中产生不需要的噪音。一些答案解决了这个问题。
在将更改提取到文件的干净版本时,我需要找到某种方法将这些更改合并到我的工作笔记本中,而无需重新运行所有内容。
(更新)
我已经考虑了几个我将在下面讨论的选项,但还没有找到一个好的综合解决方案。一个完整的解决方案可能需要对 IPython 进行一些更改,或者可能依赖于一些简单的外部脚本。我目前使用mercurial,但想要一个也适用于git 的解决方案:理想的解决方案是与版本控制无关。
这个问题已经讨论过很多次了,但是从用户的角度来看,并没有明确的或明确的解决方案。这个问题的答案应该提供明确的策略。如果它需要IPython 的最新(甚至是开发)版本或易于安装的扩展程序,那很好。
更新:我一直在玩my modified notebook 版本,它可以选择在每次保存时使用Gregory Crosswhite's suggestions 保存.clean
版本。这满足了我的大部分限制,但未解决以下问题:
-
这还不是标准解决方案(需要修改 ipython 源代码。有没有办法通过简单的扩展来实现此行为?需要某种 on-save 挂钩。
我在当前工作流程中遇到的一个问题是拉取更改。这些将进入
.clean
文件,然后需要以某种方式集成到我的工作版本中。 (当然,我总是可以重新执行笔记本,但这可能会很痛苦,尤其是如果某些结果依赖于长计算、并行计算等。)我还不知道如何解决这个问题.也许涉及ipycache 之类的扩展的工作流可能会起作用,但这似乎有点太复杂了。
注意事项
移除(剥离)输出
当笔记本运行时,可以使用Cell/All Output/Clear
菜单选项来删除输出。
有一些用于删除输出的脚本,例如脚本nbstripout.py 会删除输出,但不会产生与使用笔记本界面相同的输出。这最终包含在 ipython/nbconvert 存储库中,但已关闭说明更改现在包含在 ipython/ipython 中,但似乎尚未包含相应的功能。 (更新) 话虽如此,Gregory Crosswhite's solution 表明这很容易做到,即使不调用 ipython/nbconvert,所以如果可以正确挂钩,这种方法可能是可行的。(附加它然而,对于每个版本控制系统来说,这似乎不是一个好主意——这应该以某种方式与笔记本机制挂钩。)
新闻组
Thoughts on the notebook format for version control。问题
977: Notebook feature requests (Open)。 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)。) 1563: clear_output improvements (Merged)。 3065: diff-ability of notebooks (Closed)。 3291: Add the option to skip output cells when saving. (Closed)。这似乎非常相关,但是由于建议使用“清洁/涂抹”过滤器而被关闭。一个相关的问题what can you use if you want to strip off output before running git diff? 似乎没有得到回答。 3312: WIP: Notebook save hooks (Closed). 3747: ipynb -> ipynb transformer (Closed)。这是在 4175 中重新设置的。 4175: nbconvert: Jinjaless exporter base (Merged)。 142: Use STDIN in nbstripout if no input is given (Open)。【问题讨论】:
一旦你有一个用于删除输出的工作脚本,你可以在提交之前使用一个 Git “clean”过滤器自动应用它(参见 clean/smudge filters)。 @foobarbecue 该问题包含不令人满意的解决方法:每个问题都有至少一个限制。现在 PR 4175 已经合并了,大概可以制定一个完整的解决方案,但这仍然需要做。只要我有时间,如果其他人在此期间没有提供令人满意的解决方案,我会做(作为答案)。 @saroele 我还没有找到推荐的解决方案:我打算使用--script
选项,但它已被删除。我正在等到保存后挂钩 (which are planned) 实施后,我认为我将能够提供一个结合多种技术的可接受的解决方案。
@mforbes 看起来 PR 在您发表评论几天后才被合并。您或比我更有知识的人能否在这里发布一个答案,说明如何使用新功能?
@kobejohn:我刚刚添加了一个答案
【参考方案1】:
这是我使用 git 的解决方案。它允许您像往常一样添加和提交(和差异):这些操作不会改变您的工作树,同时(重新)运行笔记本不会改变您的 git 历史记录。
虽然这可能适用于其他 VCS,但我知道它不能满足您的要求(至少与 VSC 无关)。尽管如此,它对我来说还是很完美的,虽然它并没有什么特别出色的地方,而且很多人可能已经在使用它,但我没有找到关于如何通过谷歌搜索来实现它的明确说明。所以它可能对其他人有用。
在某处使用this content 保存文件(对于以下内容,我们假设~/bin/ipynb_output_filter.py
)
使其可执行 (chmod +x ~/bin/ipynb_output_filter.py
)
创建文件~/.gitattributes
,内容如下
*.ipynb 过滤器=dropoutput_ipynb
运行以下命令:
git config --global core.attributesfile ~/.gitattributes git config --global filter.dropoutput_ipynb.clean ~/bin/ipynb_output_filter.py git config --global filter.dropoutput_ipynb.smudge cat
完成!
限制:
它只适用于 git 在 git 中,如果你在分支somebranch
并且你在 git checkout otherbranch; git checkout somebranch
中,你通常希望工作树保持不变。在这里,您将丢失两个分支之间来源不同的笔记本的输出和单元格编号。
更一般地说,输出根本没有版本控制,就像 Gregory 的解决方案一样。为了不只是在每次您执行任何涉及结帐的操作时都将其丢弃,可以通过将其存储在单独的文件中来更改方法(但请注意,在运行上述代码时,提交 ID 是未知的!),并可能对它们进行版本控制(但请注意,这需要的不仅仅是 git commit notebook_file.ipynb
,尽管它至少可以使 git diff notebook_file.ipynb
免受 base64 垃圾的影响)。
也就是说,顺便提一下,如果您确实提取了包含某些输出的代码(即由其他人不使用此方法提交),则输出将被正常检出。只有本地生产的输出会丢失。
我的解决方案反映了这样一个事实,即我个人不喜欢对生成的内容进行版本控制 - 请注意,进行涉及输出的合并几乎可以保证使输出无效或您的生产力或 两者都有。
编辑:
如果您确实采用了我建议的解决方案 - 即全局 - 如果您想要对某些 git repo 进行版本输出,您将遇到麻烦。因此,如果您想禁用特定 git 存储库的输出过滤,只需在其中创建一个文件 .git/info/attributes,使用
**.ipynb 过滤器=
作为内容。显然,以同样的方式可以做相反的事情:为特定存储库启用过滤only。
代码现在维护在自己的git repo
如果上述说明导致 ImportErrors,请尝试在脚本路径前添加“ipython”:
git config --global filter.dropoutput_ipynb.clean ipython ~/bin/ipynb_output_filter.py
编辑:2016 年 5 月(2017 年 2 月更新):我的脚本有几个替代方案 - 为了完整起见,这里列出了我知道的那些:nbstripout (other variants )、nbstrip、jq。
【讨论】:
您如何处理合并您提取的更改的问题?您是否只需要重新生成所有输出? (我认为这是您的第二个限制的表现。) @zhermes:这个扩展版应该没问题 有没有办法将此 git 过滤器方法与外部差异工具一起使用?如果我使用普通命令行工具,则会应用过滤器,但如果我使用 meld 作为差异工具,则不会应用。 ***.com/q/30329615/578770 为了避免得到ImportError
,我不得不对上面的内容进行更改以使用ipython运行:git config --global filter.dropoutput_ipynb.clean ipython ~/bin/ipynb_output_filter.py
很棒的解决方案 Pietro,谢谢 :) 在我的案例中使用您的脚本时,我更改了 2 件事:1) 我更喜欢在回购根目录中的 .gitattributes 中声明过滤器,而不是 ~/.gitattributes
,英石其他人有和我一样的过滤器 2)我将正则表达式定义为workdir/**/*.ipynb filter=dropoutput_ipynb
,我把我的大部分笔记本放在 workdir/ => 如果我仍然想用输出推送一个笔记本并享受 github 中的可书签渲染,我只是把它放在那个文件夹之外。【参考方案2】:
我们有一个合作项目,产品是 Jupyter Notebooks,在过去的六个月里,我们使用了一种效果很好的方法:我们自动激活保存 .py
文件并跟踪 .ipynb
文件和.py
文件。
这样,如果有人想查看/下载最新的笔记本,他们可以通过 github 或 nbviewer 进行,如果有人想查看笔记本代码的变化,他们只需查看 .py
的更改即可文件。
对于Jupyter
笔记本服务器,这可以通过添加行来完成
import os
from subprocess import check_call
def post_save(model, os_path, contents_manager):
"""post-save hook for converting notebooks to .py scripts"""
if model['type'] != 'notebook':
return # only do this for notebooks
d, fname = os.path.split(os_path)
check_call(['jupyter', 'nbconvert', '--to', 'script', fname], cwd=d)
c.FileContentsManager.post_save_hook = post_save
到jupyter_notebook_config.py
文件并重新启动笔记本服务器。
如果您不确定在哪个目录中可以找到您的jupyter_notebook_config.py
文件,您可以输入jupyter --config-dir
,如果您在该目录中找不到该文件,您可以通过输入jupyter notebook --generate-config
来创建它。
对于Ipython 3
笔记本服务器,这可以通过添加行来完成
import os
from subprocess import check_call
def post_save(model, os_path, contents_manager):
"""post-save hook for converting notebooks to .py scripts"""
if model['type'] != 'notebook':
return # only do this for notebooks
d, fname = os.path.split(os_path)
check_call(['ipython', 'nbconvert', '--to', 'script', fname], cwd=d)
c.FileContentsManager.post_save_hook = post_save
到ipython_notebook_config.py
文件并重新启动笔记本服务器。这些行来自 github 问题答案@minrk provided,@dror 也将它们包含在他的 SO 答案中。
对于 Ipython 2
笔记本服务器,这可以通过使用以下命令启动服务器来完成:
ipython notebook --script
或通过添加行
c.FileNotebookManager.save_script = True
到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。
我们对此非常满意。
【讨论】:
感谢补充证据表明使用--script
在实践中有效。这样做的问题是,如果保留图像,实际的笔记本可能会很大。沿这种方式的理想解决方案可能会使用 git-annex 之类的东西来仅跟踪最新的完整笔记本。
在 Ipython 3.x 中,--script
已被弃用。 ipython.org/ipython-doc/3/whatsnew/version3.html
更新: 由于 iPython 中 Jupyter 的“大分裂”,此解决方案在 iPython 版本 4 中被破坏。要将此解决方案调整为版本 4,请使用命令 jupyter notebook --generate-config
创建配置文件。命令jupyter --config-dir
找出哪个目录包含配置文件。并且@Rich给出的代码sn-p应该添加到名为jupyter_notebook_config.py
的文件中。其余的和以前一样工作。
除了@mobiusdumpling 的要点,将check_call(['ipython'
替换为check_call(['jupyter'
,否则您将收到警告ipython nbconvert
已弃用,您应该改用jupyter nbconvert
。 (Jupyter v4.1.0, iPython v4.1.2)
如果要将.py
文件保存到当前目录以外的其他目录,请将'--output-dir', 'your_dir'
添加到check_call
。例如,check_call(['jupyter', 'nbconvert', '--to', 'script', fname, '--output-dir', './src'], cwd=d)
【参考方案3】:
我基于MinRKs gist 创建了nbstripout
,它同时支持Git 和Mercurial(感谢mforbes)。它既可以在命令行上独立使用,也可以作为过滤器使用,可以通过nbstripout install
/nbstripout uninstall
轻松(卸载)安装在当前存储库中。
从PyPI 或直接获取它
pip install nbstripout
【讨论】:
我正在考虑使用上述保存后挂钩自动创建的 .ipynb 和相应的 .py 的工作流程。我想将 .py 用于差异 - nbstripout 是否能够从单元执行计数器中清除 .py 文件(# In[1] 更改为 In[*]),这样它们就不会弄乱差异或者我应该为此创建一个简单的脚本? @KrzysztofSłowiński 不,nbstripout
不容易支持此用例,因为它依赖于 Notebook 的 JSON 格式。您最好编写一个专门针对您的用例的脚本。
nbstripout
是否可以选择在给定文件夹上递归工作(我说的是可执行文件本身)?
不是直接的,也不需要。您可以简单地使用find
或其他一些递归查找要操作的文件的标准方法。【参考方案4】:
由于存在很多策略和工具来处理笔记本的版本控制,我尝试创建一个流程图来选择合适的策略(创建于 2019 年 4 月)
【讨论】:
【参考方案5】:与 2019 年更好的方法相比,上面非常流行的 2016 年答案是不一致的黑客攻击。
存在多种选择,最能回答问题的是 Jupytext。
Jupytext
赶上Towards Data Science article on Jupytext
它与版本控制一起工作的方式是将 .py 和 .ipynb 文件都放在版本控制中。如果您想要输入差异,请查看 .py,如果您想要最新的渲染输出,请查看 .ipynb。
值得注意的提及:VS studio、nbconvert、nbdime、hydro
我认为通过更多的工作,VS studio 和/或hydrogen(或类似的)将成为此工作流程解决方案中的主导者。
【讨论】:
【参考方案6】:在删除笔记本中的输出几年后,我试图提出一个更好的解决方案。我现在使用 Jupytext,这是我为 Jupyter Notebook 和 Jupyter Lab 设计的扩展。
Jupytext 可以将 Jupyter 笔记本转换为各种文本格式(Scripts、Markdown 和 R Markdown)。反之亦然。它还提供将笔记本配对与其中一种格式的选项,并自动同步笔记本的两种表示形式(.ipynb
和 .md/.py/.R
文件)。
让我解释一下 Jupytext 是如何回答上述问题的:
允许我在包含或排除输出之间进行选择,
.md/.py/.R
文件仅包含输入单元格。您应该始终跟踪此文件。仅当您想跟踪输出时才对 .ipynb
文件进行版本控制。
防止我在不需要时意外提交输出,
将*.ipynb
添加到.gitignore
允许我将输出保留在本地版本中,
输出保存在(本地).ipynb
文件中
允许我使用我的版本控制系统查看输入何时发生更改(即,如果我只对输入进行版本控制但我的本地文件有输出,那么我希望能够查看输入是否已更改(需要提交)。使用版本控制状态命令将始终记录差异,因为本地文件有输出。)
.py/.R
或 .md
文件上的差异是您要查找的内容
允许我从更新的干净笔记本更新我的工作笔记本(包含输出)。 (更新)
拉取.py/.R
或.md
文件的最新版本并在 Jupyter (Ctrl+R) 中刷新您的笔记本。您将从文本文件中获得最新的输入单元格,以及来自.ipynb
文件的匹配输出。内核不受影响,这意味着您的局部变量被保留 - 您可以继续从离开它的地方工作。
我喜欢 Jupytext 的原因是可以在您喜欢的 IDE 中编辑笔记本(以 .py/.R
或 .md
文件的形式)。使用这种方法,重构笔记本变得容易。完成后,您只需在 Jupyter 中刷新笔记本即可。
如果您想尝试一下:使用 pip install jupytext
安装 Jupytext,然后重新启动您的 Jupyter Notebook 或 Lab 编辑器。打开您要进行版本控制的笔记本,并使用 Jupyter 笔记本中的 Jupytext Menu(或 Jupyter 实验室中的 Jupytext commands)将其配对到 Markdown 文件(或脚本)。保存你的笔记本,你会得到两个文件:原始的.ipynb
,加上承诺的笔记本文本表示,非常适合版本控制!
对于那些可能感兴趣的人:Jupytext 也可以在command line 上获得。
【讨论】:
【参考方案7】:更新:现在你可以edit Jupyter Notebook 文件直接在 Visual Studio Code 中。您可以选择编辑 笔记本或转换后的python文件。
我终于找到了一种高效且简单的方法,可以让 Jupyter 和 Git 很好地协同工作。我仍处于第一步,但我已经认为它比所有其他复杂的解决方案要好得多.
Visual Studio Code 是来自 Microsoft 的一款酷炫的开源代码编辑器。它有一个出色的 Python 扩展,现在允许您将 import a Jupyter Notebook 作为 Python 代码。现在您也可以直接edit Jupyter Notebooks。
将 notebook 导入 python 文件后,所有代码和 markdown 将一起放在一个普通的 python 文件中,并在 cmets 中带有特殊标记。您可以在下图中看到:
您的 python 文件只有笔记本输入单元格的内容。输出将在拆分窗口中生成。您在笔记本中有纯代码,它不会在您执行时更改。没有与您的代码混合的输出。没有奇怪的 JSON 难以理解的格式来分析您的差异。
只是纯 Python 代码,您可以在其中轻松识别每个差异。
我什至不再需要对我的.ipynb
文件进行版本控制。我可以在.gitignore
中添加*.ipynb
行。
需要生成笔记本以发布或与他人共享?没问题,只需在交互式 python 窗口中click the export button
如果您直接编辑笔记本,现在有一个图标Convert and save to a python script
。
这是 Visual Studio Code 中笔记本的屏幕截图:
我只用了一天,但终于可以愉快地将 Jupyter 与 Git 一起使用。
P.S.:VSCode 代码补全比 Jupyter 好很多。
【讨论】:
你知道这是如何导出为 pdf 的吗?它使用的实际命令是什么?使用 vscode 时,我可以转换为 pdf 并保留 matplotlib 图。但是,当使用 jupyterlab 时,生成的 pdf 不会保留任何输出。理想情况下,我想使用 jupytext 生成没有代码但输出的 pdf @bryce,我不知道。但是看看纯 Jupyter 而不是 JupyterLab。我认为它的导出功能效果更好。【参考方案8】:(2017-02)
策略
on_commit(): 剥离输出> name.ipynb (nbstripout
, )
剥离输出> name.clean.ipynb (nbstripout
,)
总是nbconvert
到python:name.ipynb.py (nbconvert
)
始终转换为降价:name.ipynb.md (nbconvert
, ipymd
)
vcs.configure():
git difftool、mergetool:来自 nbdime 的 nbdiff 和 nbmerge
工具
nbstripout
:从笔记本中剥离输出
源代码:https://gist.github.com/minrk/6176788
源代码:https://github.com/kynan/nbstripout
pip install nbstripout; nbstripout install
ipynb_output_filter
:从笔记本中剥离输出
源代码:https://github.com/toobaz/ipynb_output_filter/blob/master/ipynb_output_filter.py
ipymd
:在 Jupyter、Markdown、O'Reilly Atlas Markdown、OpenDocument、.py 之间转换
源代码:https://github.com/rossant/ipymd
nbdime
:“用于区分和合并 Jupyter 笔记本的工具。” (2015)
源代码:https://github.com/jupyter/nbdime
文档:http://nbdime.readthedocs.io/
nbdiff
:以终端友好的方式比较笔记本
nbdime nbdiff 用作 git diff 工具:https://nbdime.readthedocs.io/en/latest/#git-integration-quickstart
nbmerge
:笔记本三路合并,自动解决冲突
nbdime nbmerge 用作 git 合并工具
nbdiff-web
: 向您展示笔记本的丰富渲染差异
nbmerge-web
:给你一个基于网络的笔记本三向合并工具
nbshow
:以终端友好的方式呈现单个笔记本
【讨论】:
【参考方案9】:这是 Cyrille Rossant 为 IPython 3.0 提供的一个新解决方案,它保留在 markdown 文件而不是基于 json 的 ipymd 文件:
https://github.com/rossant/ipymd
【讨论】:
似乎还不支持 Jupyter。 我正在将 ipymd 与最新的 Jupyter 一起成功使用——您是否收到任何具体问题或错误消息?【参考方案10】:刚刚遇到“jupytext”,它看起来像是一个完美的解决方案。它从笔记本生成一个 .py 文件,然后保持两者同步。您可以通过 .py 文件进行版本控制、差异化和合并输入,而不会丢失输出。当您打开笔记本时,它使用 .py 输入单元格和 .ipynb 输出单元格。如果您想在 git 中包含输出,则只需添加 ipynb。
https://github.com/mwouts/jupytext
【讨论】:
【参考方案11】:正如所指出的,--script
在3.x
中已弃用。可以通过应用保存后挂钩来使用此方法。特别是,将以下内容添加到ipython_notebook_config.py
:
import os
from subprocess import check_call
def post_save(model, os_path, contents_manager):
"""post-save hook for converting notebooks to .py scripts"""
if model['type'] != 'notebook':
return # only do this for notebooks
d, fname = os.path.split(os_path)
check_call(['ipython', 'nbconvert', '--to', 'script', fname], cwd=d)
c.FileContentsManager.post_save_hook = post_save
代码取自#8009。
【讨论】:
感谢您演示如何使用保存后挂钩。不幸的是,正如前面提到的,从.py
文件返回到笔记本是有问题的,所以很遗憾这不是一个完整的解决方案。 (我有点希望这是因为区分 .py
文件而不是笔记本非常好。也许新的 notebook diff 功能会很有用。
谢谢!我现在正在使用这个技巧来重现 --script
行为,而不管版本控制如何。起初我遇到了一些问题,所以以防万一我可以节省一些时间:1)如果配置文件文件夹中缺少ipython_notebook_config.py
,请运行ipython profile create
来生成它。 2) 如果似乎忽略了保存后挂钩,请使用--debug
运行 ipython 来诊断问题。 3) 如果脚本失败并出现错误ImportError: No module named mistune
- 简单安装 minstue: pip install mistune
.【参考方案12】:
很遗憾,我对 Mercurial 了解不多,但我可以为您提供一个适用于 Git 的可能解决方案,希望您能够将我的 Git 命令翻译成与 Mercurial 等效的命令。
作为背景,在 Git 中,add
命令将对文件所做的更改存储到暂存区域。完成此操作后,Git 将忽略对该文件的任何后续更改,除非您告诉它也将它们暂存。因此,以下脚本会针对每个给定文件剥离所有 outputs
和 prompt_number sections
,暂存剥离的文件,然后恢复原始文件:
注意:如果运行它会得到类似ImportError: No module named IPython.nbformat
的错误消息,那么使用ipython
来运行脚本而不是python
。
from IPython.nbformat import current
import io
from os import remove, rename
from shutil import copyfile
from subprocess import Popen
from sys import argv
for filename in argv[1:]:
# Backup the current file
backup_filename = filename + ".backup"
copyfile(filename,backup_filename)
try:
# Read in the notebook
with io.open(filename,'r',encoding='utf-8') as f:
notebook = current.reads(f.read(),format="ipynb")
# Strip out all of the output and prompt_number sections
for worksheet in notebook["worksheets"]:
for cell in worksheet["cells"]:
cell.outputs = []
if "prompt_number" in cell:
del cell["prompt_number"]
# Write the stripped file
with io.open(filename, 'w', encoding='utf-8') as f:
current.write(notebook,f,format='ipynb')
# Run git add to stage the non-output changes
print("git add",filename)
Popen(["git","add",filename]).wait()
finally:
# Restore the original file; remove is needed in case
# we are running in windows.
remove(filename)
rename(backup_filename,filename)
在您要提交更改的文件上运行脚本后,只需运行 git commit
。
【讨论】:
感谢您的建议。 Mercurial 并没有像 git 这样的临时区域(尽管为此目的可以使用 mercurial queues)。同时,我尝试将此代码添加到保存挂钩中,该挂钩保存带有.clean
扩展名的干净版本。不幸的是,如果没有directly modifying IPython,我看不到如何做到这一点(尽管这种变化非常微不足道)。我会玩一会儿,看看它是否适合我的所有需求。【参考方案13】:
我使用非常务实的方法;这适用于几个笔记本,在几个方面。它甚至使我能够“转移”笔记本。它适用于 Windows 和 Unix/MacOS。 阿尔觉得很简单,就是解决上面的问题……
概念
基本上,不跟踪.ipnyb
-文件,只跟踪相应的.py
-文件。
通过使用--script
选项启动notebook-server,在保存笔记本时会自动创建/保存该文件。
那些.py
-文件确实包含所有输入;非代码保存到 cmets 中,单元格边框也是如此。可以将这些文件读取/导入(并拖动)到笔记本服务器中以(重新)创建笔记本。只有输出消失了;直到重新运行。
我个人使用 mercurial 对.py
文件进行版本跟踪;并使用普通(命令行)命令添加,签入(等)。大多数其他 (D)VCS 将允许这样做。
现在追踪历史很简单; .py
很小,有文字且易于区分。有时,我们需要一个克隆(只是分支;在那里启动第二个笔记本服务器)或旧版本(签出并导入笔记本服务器)等等。
提示和技巧
将 *.ipynb 添加到“.hgignore”,以便 Mercurial 知道它可以忽略这些文件 创建一个 (bash) 脚本来启动服务器(使用--script
选项)并对其进行版本跟踪
保存笔记本确实保存了.py
-文件,但不将其签入。
这是一个缺点:人们可以忘记这一点
这也是一个功能:无需对存储库历史进行集群即可保存笔记本(并稍后继续)。
愿望
如果笔记本仪表板中有一个用于签入/添加/等的按钮会很不错 结帐到(通过示例)file@date+rev.py
)应该会有所帮助
添加它会做很多工作;也许我会这样做一次。到目前为止,我只是手动完成。
【讨论】:
如何从.py
文件返回到笔记本?我喜欢这种方法,但因为.ipynb
-> .py
-> .ipynb
可能有损耗,所以我没有认真考虑这一点。
这很简单:加载它,例如将它放到笔记本仪表板上。除了“输出数据”,什么都没有丢失
如果这是真的,那么我认为这很接近想法,但我似乎记得 IPython 没有承诺在从 .py
到 .ipynb
格式的转换中完全保留数据。有一个issue about this - 所以也许这将构成一个完整解决方案的基础。
我在将 .py
文件转换为 .ipynb
文件时遇到了一些困难。 nbconvert
似乎还不支持这一点,而且我没有笔记本仪表板,因为我手动运行 ipython notebook
。对于如何实现这种向后转换,您有什么一般性建议吗?
.py
-to-notebook 转换肯定不是为了往返。所以这并不是一个通用的解决方案,尽管它很适合你。【参考方案14】:
我已经构建了解决这个问题的python包
https://github.com/***isme/gitnb
它提供了一个带有受 git 启发的语法的 CLI 来跟踪/更新/区分 git 存储库中的笔记本。
这是一个例子
# add a notebook to be tracked
gitnb add SomeNotebook.ipynb
# check the changes before commiting
gitnb diff SomeNotebook.ipynb
# commit your changes (to your git repo)
gitnb commit -am "I fixed a bug"
请注意,我使用“gitnb commit”的最后一步是提交到您的 git 存储库。它本质上是
的包装器# get the latest changes from your python notebooks
gitnb update
# commit your changes ** this time with the native git commit **
git commit -am "I fixed a bug"
还有更多方法,可以配置为在每个阶段需要或多或少的用户输入,但这是大体思路。
【讨论】:
【参考方案15】:我还将向其他人添加https://nbdev.fast.ai/,这是最先进的“文学编程环境,正如 Donald Knuth 在 1983 年所设想的那样!”。
它还有一些 git 钩子可以帮助https://nbdev.fast.ai/#Avoiding-and-handling-git-conflicts 和其他命令,例如:
nbdev_read_nbs nbdev_clean_nbs nbdev_diff_nbs nbdev_test_nbs因此,您还可以在编写库时随时随地创建文档,例如其中一些:
https://dev.fast.ai/ https://ohmeow.github.io/blurr/ https://rbracco.github.io/fastai2_audio/除了第一个链接之外,您还可以在 nbdev tutorial 观看视频。
【讨论】:
我没有机会深入研究,但这似乎不支持 Knuth 所说的“缠结”,这是识字编程的主要观点之一。这使您可以按照对解释有意义的顺序编写代码,同时保留磁盘上所需的适当顺序。例如14_callback.schedule.ipynb 似乎从导入语句开始——代码中最不重要的部分。缠结允许您将其推迟到描述主要概念之后。 好吧,不太确定它是否确实处理tangling
,但从该文件生成的“真实”python 文件是fastai2/callback/schedule.py,我添加了一个我没看过的youtube 视频。
【参考方案16】:
如果您遇到这样的 Unicode 解析错误,请跟进 Pietro Battiston 的出色脚本:
Traceback (most recent call last):
File "/Users/kwisatz/bin/ipynb_output_filter.py", line 33, in <module>
write(json_in, sys.stdout, NO_CONVERT)
File "/Users/kwisatz/anaconda/lib/python2.7/site-packages/IPython/nbformat/__init__.py", line 161, in write
fp.write(s)
UnicodeEncodeError: 'ascii' codec can't encode character u'\u2014' in position 11549: ordinal not in range(128)
您可以在脚本开头添加:
reload(sys)
sys.setdefaultencoding('utf8')
【讨论】:
【参考方案17】:挖了一圈,终于找到this relatively simple pre-save hook on the Jupyter docs。它剥离单元格输出数据。您必须将其粘贴到 jupyter_notebook_config.py
文件中(请参阅下面的说明)。
def scrub_output_pre_save(model, **kwargs):
"""scrub output before saving notebooks"""
# only run on notebooks
if model['type'] != 'notebook':
return
# only run on nbformat v4
if model['content']['nbformat'] != 4:
return
for cell in model['content']['cells']:
if cell['cell_type'] != 'code':
continue
cell['outputs'] = []
cell['execution_count'] = None
# Added by binaryfunt:
if 'collapsed' in cell['metadata']:
cell['metadata'].pop('collapsed', 0)
c.FileContentsManager.pre_save_hook = scrub_output_pre_save
来自Rich Signell's answer:
如果您不确定在哪个目录中可以找到您的
jupyter_notebook_config.py
文件,您可以输入jupyter --config-dir
[进入命令提示符/终端],如果您在该目录中找不到该文件,您可以通过以下方式创建它输入jupyter notebook --generate-config
。
【讨论】:
我会注意到这个解决方案永远不会将任何输出保存到磁盘,并且在某种程度上独立于版本控制问题。【参考方案18】:这是 2020 年 4 月,有很多用于 Jupyter 笔记本版本控制的策略和工具。以下是您可以使用的所有工具的快速概览,
nbdime - 非常适合笔记本的本地差异和合并
nbstripout - 一个 git 过滤器,用于在每次提交之前自动删除笔记本输出
jupytext - 将 .py 配套文件同步到每个笔记本。你只提交 .py 文件
nbconvert - 将笔记本转换为 python 脚本或 HTML(或两者)并提交这些备用文件类型
ReviewNB - 显示 GitHub 上任何提交或拉取请求的笔记本差异(连同输出)。还可以在笔记本单元格上编写 cmets 来讨论更改(下面的屏幕截图)。
免责声明:我构建了 ReviewNB。
【讨论】:
【参考方案19】:我做了 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) 笔记本中还有其他东西,例如您丢失的 markdown。我改为使用 git 过滤器从笔记本中剥离输出。讨论如何做到这一点是here。
【讨论】:
我喜欢这个想法,但经过测试,发现从.py
文件转换回.ipynb
是有问题的,特别是对于还没有转换器的版本4 笔记本。目前需要使用 v3 导入器,然后转换为 v4,我有点担心这个复杂的行程。此外,如果笔记本主要是 Julia 代码,.py
文件也不是一个很好的选择!最后,--script
已被弃用,所以我认为钩子是要走的路。
您链接中的 git 过滤器解决方案很好,您应该从此处复制您的答案:-)【参考方案20】:
好的,根据here 的讨论,目前最好的解决方案是制作一个 git 过滤器,以便在提交时自动去除 ipynb 文件的输出。
这是我为使其正常工作所做的工作(从该讨论中复制):
当您无法导入最新的 IPython 时,我稍微修改了 cfriedline 的 nbstripout 文件以提供信息性错误:
https://github.com/petered/plato/blob/fb2f4e252f50c79768920d0e47b870a8d799e92b/notebooks/config/strip_notebook_output
并将其添加到我的仓库中,比如./relative/path/to/strip_notebook_output
还将文件 .gitattributes 文件添加到 repo 的根目录,包含:
*.ipynb filter=stripoutput
并创建了一个setup_git_filters.sh
包含
git config filter.stripoutput.clean "$(git rev-parse --show-toplevel)/relative/path/to/strip_notebook_output"
git config filter.stripoutput.smudge cat
git config filter.stripoutput.required true
然后跑了source setup_git_filters.sh
。花哨的 $(git rev-parse...) 事情是在任何(Unix)机器上找到你的 repo 的本地路径。
【讨论】:
【参考方案21】:您可以使用这个 jupyter 扩展。它将使您能够直接将您的 ipython 笔记本上传到 github。
https://github.com/sat28/githubcommit
我还制作了一个视频来演示这些步骤 - youtube link
【讨论】:
你能解释一下这是做什么的吗?记录不是特别清楚。 @AlexMonras 这将直接在 jupyter notebook 中添加一个按钮,您可以从该按钮将 notebook 推送到您的 GitHub 存储库并带有提交消息【参考方案22】:下面帖子中讨论的想法怎么样,笔记本的输出应该保存在哪里,并认为生成它可能需要很长时间,而且它很方便,因为 GitHub 现在可以渲染笔记本。添加了用于导出 .py 文件的自动保存挂钩,用于 diffs 和 .html 用于与不使用笔记本或 git 的团队成员共享。
https://towardsdatascience.com/version-control-for-jupyter-notebook-3e6cef13392d
【讨论】:
以上是关于在版本控制下使用 IPython / Jupyter Notebooks的主要内容,如果未能解决你的问题,请参考以下文章
PythonXY,IPython Qt 控制台,matplotlib,绘制不在内联模式下的东西
Spyder IDE 中的 iPython 控制台挂起任何代码执行
如何在 Anaconda 2.0 中使用 Python 3.4 激活 Ipython Notebook 和 QT 控制台