<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Konrad Lyda</title>
    <description>The latest articles on DEV Community by Konrad Lyda (@klyda).</description>
    <link>https://dev.to/klyda</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F846868%2Fa1ddc11f-33ba-4a12-9ab1-d7ed993e101b.jpeg</url>
      <title>DEV Community: Konrad Lyda</title>
      <link>https://dev.to/klyda</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/klyda"/>
    <language>en</language>
    <item>
      <title>How To Merge Jupyter Notebooks in Git Repo Without a Headache?</title>
      <dc:creator>Konrad Lyda</dc:creator>
      <pubDate>Fri, 28 Oct 2022 22:01:13 +0000</pubDate>
      <link>https://dev.to/klyda/how-to-merge-jupyter-notebooks-in-git-repo-without-a-headache-5a73</link>
      <guid>https://dev.to/klyda/how-to-merge-jupyter-notebooks-in-git-repo-without-a-headache-5a73</guid>
      <description>&lt;p&gt;How To Merge Jupyter Notebooks in Git Repo Without a Headache?&lt;/p&gt;

&lt;p&gt;Have you ever felt this faster heart beating, when you had to merge one git branch to another, and it turned out, that there are diffs in Jupyter Notebooks? Mike did.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KZ7ktqN3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://tailored.ml/assets/img/posts/1%2AzR0QF6gKUlQkvWGRb5KxAg.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KZ7ktqN3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://tailored.ml/assets/img/posts/1%2AzR0QF6gKUlQkvWGRb5KxAg.jpeg" alt="" width="480" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let me show you one tool today that will make your life easier. It only takes 1 minute to avoid having to look at images stored in base64 in JSON files (it made me weak already…).&lt;/p&gt;

&lt;p&gt;We’ll start smoothly, with just comparing notebooks, then we’ll move to GUI version, where we can compare files in a browser, and finally, we’ll fire the final firecracker — notebook merge.&lt;/p&gt;

&lt;p&gt;Feel invited!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;TL;DR at the bottom of the post 😉&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  What’s in store for us? — table of contents
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Mike has had enough — what’s the problem?&lt;/li&gt;
&lt;li&gt;Conflicts in Jupyter Notebooks&lt;/li&gt;
&lt;li&gt;The rescue&lt;/li&gt;
&lt;li&gt;Comparing notebooks&lt;/li&gt;
&lt;li&gt;Merging notebooks&lt;/li&gt;
&lt;li&gt;Git integration&lt;/li&gt;
&lt;li&gt;Plugin for Jupyter&lt;/li&gt;
&lt;li&gt;A treat at the end&lt;/li&gt;
&lt;li&gt;Links&lt;/li&gt;
&lt;li&gt;TL:DR;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Mike has had enough — what’s the problem?
&lt;/h2&gt;

&lt;p&gt;Mike’s company uses GIT. Every self-respecting team uses some kind of version control system because they don’t move their code on a memory stick, right?&lt;/p&gt;

&lt;p&gt;And as you know, in GIT we have branches, and when several people work on a project, the branches need to be connected, i.e. merged. And Mike is fluent in using GIT like a crawfish.&lt;/p&gt;

&lt;p&gt;In Mike’s project, they have a python code in a very nice structure created by &lt;a href="https://drivendata.github.io/cookiecutter-data-science/#directory-structure"&gt;Data Science Cookie Cutter&lt;/a&gt;. As in every Data Science project, there is a folder called notebooks/, and inside that folder nothing else but some Jupyter Notebooks.&lt;/p&gt;

&lt;p&gt;Jupyter Notebooks are stored in JSON format. We have some text files, so it seems that the GIT repo is a good place for this kind of document.&lt;/p&gt;

&lt;p&gt;Mike has done some fixes in his python file. Standard procedure after a patch. Commit. Push. Pull Request.&lt;/p&gt;

&lt;p&gt;But after the commit, he remembered that he created his branch a long time ago, so he merged the changes from the develop branch, to make the pull (merge — for GitLab users) request work nicely.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conflicts in Jupyter Notebooks
&lt;/h2&gt;

&lt;p&gt;Someone introduced changes to Jupyter Notebook, which Roman just tweaked a bit. While trying to merge repository branches, conflicts appeared.&lt;br&gt;
Roman fired up git diff. What he saw was a forest… well, text like in the picture below.&lt;/p&gt;



&lt;p&gt;It’s not good. In fact, it’s not obvious what’s going on. So, how to choose which changes should be kept and which should not?&lt;/p&gt;

&lt;p&gt;Let’s check via some IDE or some GIT client like Sublime Merge&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TQATAhwD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://tailored.ml/assets/img/posts/1%2AHuZof7bSLZbWmOaDKrivDw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TQATAhwD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://tailored.ml/assets/img/posts/1%2AHuZof7bSLZbWmOaDKrivDw.png" alt="" width="880" height="581"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A little better, but still not good. Digging around in JSON while merging notebooks is not the most pleasant thing to do…&lt;/p&gt;

&lt;p&gt;So, what now? Does Mike stand a chance against the brutal Json mutated by Git?&lt;/p&gt;
&lt;h2&gt;
  
  
  Nbdime to the rescue!
&lt;/h2&gt;

&lt;p&gt;Nbdime is a tool to compare and merge changes in Jupyter Notebooks.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://nbdime.readthedocs.io/en/latest/"&gt;https://nbdime.readthedocs.io/en/latest/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Mike went straight to installing the tool, which, as in Python, is pretty standard:&lt;/p&gt;

&lt;p&gt;pip install nbdime&lt;/p&gt;

&lt;p&gt;Now what? What can Mike do next with the tool he installed?&lt;/p&gt;
&lt;h2&gt;
  
  
  Comparing notebooks
&lt;/h2&gt;

&lt;p&gt;The first thing Mike checked is comparing notebooks. And here we have two possibilities.&lt;/p&gt;
&lt;h3&gt;
  
  
  From the terminal level
&lt;/h3&gt;

&lt;p&gt;If we don’t have access to the graphical environment (because we are connected to a remote server via SSH), we can use the comparison displayed in the terminal. This is done with the command&lt;/p&gt;

&lt;p&gt;nbdiff notebook_1.ipynb notebook_2.ipynb&lt;/p&gt;

&lt;p&gt;Then we get something similar to:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zbbq9ig4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://tailored.ml/assets/img/posts/1%2AdQ3RGvWCeYuvTd5-Q3R-qQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zbbq9ig4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://tailored.ml/assets/img/posts/1%2AdQ3RGvWCeYuvTd5-Q3R-qQ.png" alt="Diff notebooks in the console" width="880" height="794"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Do you admit that it is better? We know that two cells were added, that one modified, and that one output was deleted. Yes, the same can be read from JSON, but I guess it’s easier from the text above, right?&lt;/p&gt;

&lt;p&gt;But it doesn’t end there because nbdiff didn’t say the last word. Let’s move on to…&lt;/p&gt;
&lt;h3&gt;
  
  
  GUI in the browser
&lt;/h3&gt;

&lt;p&gt;If we have a graphical environment (because we are on our computer or a remote machine connected via RDP), we can use the GUI. This will launch a page in the browser, where we can conveniently compare two notebooks.&lt;/p&gt;

&lt;p&gt;And here Mike’s eyes lit up as bright as the headlights on his Toyota Prius. He issued a command like the following:&lt;/p&gt;

&lt;p&gt;nbdiff-web [~~ [~~]] [~~]&lt;/p&gt;

&lt;p&gt;And after a while, the browser launched a notebook comparison he hadn’t even dreamed of!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ehwwsscX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://tailored.ml/assets/img/posts/1%2APU5feR0HqaVEqA-Rn2f5qQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ehwwsscX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://tailored.ml/assets/img/posts/1%2APU5feR0HqaVEqA-Rn2f5qQ.png" alt="Notebook diff in nbdime — nbdiff-web" width="880" height="767"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Isn’t it beautiful?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Okay, I can compare two notebooks. But what about my conflicts in git? — Mike rightly pointed out.&lt;/li&gt;
&lt;li&gt;Will nbdime help me?&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Merging notebooks
&lt;/h2&gt;

&lt;p&gt;Nbdime stands for “Jupyter &lt;strong&gt;N&lt;/strong&gt;ote*&lt;em&gt;b&lt;/em&gt;&lt;em&gt;ooks **D&lt;/em&gt;&lt;em&gt;iffing and **M&lt;/em&gt;*erging”. As we can guess, apart from seeing the changes (diff), we can also merge them!&lt;/p&gt;

&lt;p&gt;Nbdime gives us the ability to merge notebooks, that is, to combine changes that have been made to the same file by two different* people.&lt;/p&gt;

&lt;p&gt;And here is what Mike likes the most. Here, Mike’s eyes glazed over with emotion. No more merging changes in JSON or combining them with simultaneous notebooks. No more agonizing, no more moaning. Pure excitement at the simplicity of combining two notebooks into one. Just as two people on the wedding cake are now one, two notebooks that don’t quite fit together are now one…&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Show me the code! — Shouted the colleague to whom Mike wanted to show off. In the terminal, he issued the command below and muttered “ENTER”.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;nbmerge-web base.ipynb local.ipynb remote.ipynb --out merged.ipynb&lt;/p&gt;

&lt;p&gt;And to their eyes appeared a screen, as if from a dandelion:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mHD3NCG5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://tailored.ml/assets/img/posts/1%2AG6Q9U758Yd9vVmZ8ff67tA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mHD3NCG5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://tailored.ml/assets/img/posts/1%2AG6Q9U758Yd9vVmZ8ff67tA.png" alt="Merge notebooks in the browser" width="880" height="798"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With a few efficient clicks, Mike merged the changes that were occurring in the notebook. These included:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Changes to the Metadata, such as the tags for a particular cell (YES, we can merge these changes too! Isn’t it beautiful?)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--sqSiMZHP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://tailored.ml/assets/img/posts/1%2APLRNw9UczCUJMGpIh1fdyw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--sqSiMZHP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://tailored.ml/assets/img/posts/1%2APLRNw9UczCUJMGpIh1fdyw.png" alt="" width="880" height="376"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Changes that have occurred to a given cell in one of the notebooks:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--R5pUOzyB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://tailored.ml/assets/img/posts/1%2A8CbVS5T5M7c99gTCHRk08A.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--R5pUOzyB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://tailored.ml/assets/img/posts/1%2A8CbVS5T5M7c99gTCHRk08A.png" alt="" width="880" height="207"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Changes that occurred in a cell in &lt;strong&gt;both&lt;/strong&gt; notebooks:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--k97Jlknz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://tailored.ml/assets/img/posts/1%2AKQWXcO2MUR-uHn6zUM7vww.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--k97Jlknz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://tailored.ml/assets/img/posts/1%2AKQWXcO2MUR-uHn6zUM7vww.png" alt="" width="880" height="89"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deleted cells:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--UTIEANPL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://tailored.ml/assets/img/posts/1%2A-FV1NnORtUA17DyZZw0YZg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--UTIEANPL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://tailored.ml/assets/img/posts/1%2A-FV1NnORtUA17DyZZw0YZg.png" alt="" width="880" height="71"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Added cells:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JgwqiP1y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://tailored.ml/assets/img/posts/1%2A_lUk1B-phnNOM6QR7hoEzQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JgwqiP1y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://tailored.ml/assets/img/posts/1%2A_lUk1B-phnNOM6QR7hoEzQ.png" alt="" width="880" height="199"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;nbmerge&lt;/strong&gt; can use different strategies for pre-merging changes. Below, I’ve pasted the parameters you can give it to get the merge party started:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;➜ nbmerge-web
usage: nbmerge-web &lt;span class="se"&gt;\[&lt;/span&gt;&lt;span class="nt"&gt;-h&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="se"&gt;\[&lt;/span&gt;&lt;span class="nt"&gt;--version&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="se"&gt;\[&lt;/span&gt;&lt;span class="nt"&gt;--config&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="se"&gt;\[&lt;/span&gt;&lt;span class="nt"&gt;--log-level&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;DEBUG,INFO,WARN,ERROR,CRITICAL&lt;span class="o"&gt;}]&lt;/span&gt; &lt;span class="se"&gt;\[&lt;/span&gt;&lt;span class="nt"&gt;-s&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="se"&gt;\[&lt;/span&gt;&lt;span class="nt"&gt;-o&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="se"&gt;\[&lt;/span&gt;&lt;span class="nt"&gt;-a&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="se"&gt;\[&lt;/span&gt;&lt;span class="nt"&gt;-m&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="se"&gt;\[&lt;/span&gt;&lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="se"&gt;\[&lt;/span&gt;&lt;span class="nt"&gt;--merge-strategy&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;inline,use-base,use-local,use-remote&lt;span class="o"&gt;}]&lt;/span&gt;
&lt;span class="se"&gt;\[&lt;/span&gt;&lt;span class="nt"&gt;--input-strategy&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;inline,use-base,use-local,use-remote&lt;span class="o"&gt;}]&lt;/span&gt;
&lt;span class="se"&gt;\[&lt;/span&gt;&lt;span class="nt"&gt;--output-strategy&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;inline,use-base,use-local,use-remote,remove,clear-all&lt;span class="o"&gt;}]&lt;/span&gt;
&lt;span class="se"&gt;\[&lt;/span&gt;&lt;span class="nt"&gt;--no-ignore-transients&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="se"&gt;\[&lt;/span&gt;&lt;span class="nt"&gt;-p&lt;/span&gt; PORT] &lt;span class="se"&gt;\[&lt;/span&gt;&lt;span class="nt"&gt;-b&lt;/span&gt; BROWSER] &lt;span class="se"&gt;\[&lt;/span&gt;&lt;span class="nt"&gt;--persist&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="se"&gt;\[&lt;/span&gt;&lt;span class="nt"&gt;--ip&lt;/span&gt; IP] &lt;span class="se"&gt;\[&lt;/span&gt;&lt;span class="nt"&gt;-w&lt;/span&gt; WORKDIRECTORY] &lt;span class="se"&gt;\[&lt;/span&gt;&lt;span class="nt"&gt;--base-url&lt;/span&gt; BASE&lt;span class="se"&gt;\_&lt;/span&gt;URL]
&lt;span class="se"&gt;\[&lt;/span&gt;&lt;span class="nt"&gt;--show-unchanged&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="se"&gt;\[&lt;/span&gt;&lt;span class="nt"&gt;--out&lt;/span&gt; OUT]
base &lt;span class="nb"&gt;local &lt;/span&gt;remote
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Git integration
&lt;/h2&gt;

&lt;p&gt;And here is the moment that for Mike was a saviour. &lt;strong&gt;Nbdime integrates with Git&lt;/strong&gt;. And this is the feature everyone was waiting for.&lt;/p&gt;

&lt;h3&gt;
  
  
  Diff
&lt;/h3&gt;

&lt;p&gt;Nbdime can be activated per repository you work in, or globally, for all repositories and handling &lt;strong&gt;all&lt;/strong&gt; diff&lt;/p&gt;

&lt;p&gt;Roman doesn’t grind in the dance, so he activated globally. YOLO.&lt;/p&gt;

&lt;p&gt;nbdime config-git --enable --global&lt;/p&gt;

&lt;p&gt;If he wasn’t such a wuss, he could have omitted the --global parameter, and then it would only activate for the repository it was currently in.&lt;/p&gt;

&lt;h3&gt;
  
  
  Merge
&lt;/h3&gt;

&lt;p&gt;Here the situation is a bit more complicated. If we set &lt;strong&gt;nbmerge&lt;/strong&gt; as the default mergetool in git, it will handle &lt;strong&gt;all files&lt;/strong&gt;. &lt;strong&gt;We rather don’t want this to happen&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The most convenient way here, in case of conflicts, is to use the command below. This will resolve conflicts using nbdime in this particular notebook, and in code files in our primary tool (VSCode, Pycharm, VIM, whateva).&lt;/p&gt;

&lt;p&gt;git mergetool --tool=nbdime my_awesome_notebook.ipynb&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Take a look at the documentation: &lt;a href="https://nbdime.readthedocs.io/en/latest/vcs.html#git-integration"&gt;https://nbdime.readthedocs.io/en/latest/vcs.html#git-integration&lt;/a&gt;.&lt;br&gt;
 There you will find detailed description how to register nbdime as mergetool globally, selectively, etc.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;P.S. Apparently it also works with Mercurial, but does anyone else use it?&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Plugin for Jupyter
&lt;/h2&gt;

&lt;p&gt;To top it all off, Mike discovered that &lt;strong&gt;nbdime has a plugin for Jupyter Lab&lt;/strong&gt;! He didn’t even have to reach into the console any more (although he liked it a lot). He could preview notebook changes directly from the Jupyter Notebooks IDE!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--17J90TMP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://tailored.ml/assets/img/posts/1%2AayW5nfEdjmRm5WwN5xI8sQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--17J90TMP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://tailored.ml/assets/img/posts/1%2AayW5nfEdjmRm5WwN5xI8sQ.png" alt="Diff notebooks in Jupyter Lab" width="880" height="313"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here’s a link to the extension and installation details: &lt;a href="https://nbdime.readthedocs.io/en/latest/extensions.html"&gt;https://nbdime.readthedocs.io/en/latest/extensions.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Basically, just issue this command:&lt;/p&gt;

&lt;p&gt;nbdime extensions --enable [--sys-prefix/--user/--system]&lt;/p&gt;

&lt;h2&gt;
  
  
  A treat at the end — nbshow
&lt;/h2&gt;

&lt;p&gt;Things happen in life, sometimes the only thing we have available is a terminal. We have connected to a remote machine via SSH. We want to view a notebook. Now what? Again, watching JSONs that don’t speak much?&lt;/p&gt;

&lt;p&gt;This is where &lt;strong&gt;nbshow&lt;/strong&gt; comes in handy.&lt;/p&gt;

&lt;p&gt;An example nbshow output might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;➜ nbshow prep&lt;span class="se"&gt;\_&lt;/span&gt;data/image&lt;span class="se"&gt;\_&lt;/span&gt;data&lt;span class="se"&gt;\_&lt;/span&gt;guide/03c&lt;span class="se"&gt;\_&lt;/span&gt;pytorch&lt;span class="se"&gt;\_&lt;/span&gt;preprocessing.ipynb
notebook format: 4.4
metadata &lt;span class="o"&gt;(&lt;/span&gt;known keys&lt;span class="o"&gt;)&lt;/span&gt;:
kernelspec:
display&lt;span class="se"&gt;\_&lt;/span&gt;name: conda&lt;span class="se"&gt;\_&lt;/span&gt;pytorch&lt;span class="se"&gt;\_&lt;/span&gt;latest&lt;span class="se"&gt;\_&lt;/span&gt;p36
language: python
name: conda&lt;span class="se"&gt;\_&lt;/span&gt;pytorch&lt;span class="se"&gt;\_&lt;/span&gt;latest&lt;span class="se"&gt;\_&lt;/span&gt;p36

&lt;span class="se"&gt;\[&lt;/span&gt;...]

code cell 25:
execution&lt;span class="se"&gt;\_&lt;/span&gt;count: 10
&lt;span class="nb"&gt;source&lt;/span&gt;:
sample &lt;span class="o"&gt;=&lt;/span&gt; iter&lt;span class="o"&gt;(&lt;/span&gt;sample&lt;span class="o"&gt;)&lt;/span&gt;
sample&lt;span class="se"&gt;\_&lt;/span&gt;augmented &lt;span class="o"&gt;=&lt;/span&gt; iter&lt;span class="o"&gt;(&lt;/span&gt;sample&lt;span class="se"&gt;\_&lt;/span&gt;augmented&lt;span class="o"&gt;)&lt;/span&gt;
markdown cell 26:
&lt;span class="nb"&gt;source&lt;/span&gt;:
Re-rull the cell below to sample another image
code cell 27:
execution&lt;span class="se"&gt;\_&lt;/span&gt;count: 11
&lt;span class="nb"&gt;source&lt;/span&gt;:
fig, ax &lt;span class="o"&gt;=&lt;/span&gt; plt.subplots&lt;span class="o"&gt;(&lt;/span&gt;1, 2, &lt;span class="nv"&gt;figsize&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;10,5&lt;span class="o"&gt;))&lt;/span&gt;
image &lt;span class="o"&gt;=&lt;/span&gt; next&lt;span class="o"&gt;(&lt;/span&gt;iter&lt;span class="o"&gt;(&lt;/span&gt;sample&lt;span class="o"&gt;))&lt;/span&gt;&lt;span class="se"&gt;\[&lt;/span&gt;0]
image&lt;span class="se"&gt;\_&lt;/span&gt;augmented &lt;span class="o"&gt;=&lt;/span&gt; next&lt;span class="o"&gt;(&lt;/span&gt;iter&lt;span class="o"&gt;(&lt;/span&gt;sample&lt;span class="se"&gt;\_&lt;/span&gt;augmented&lt;span class="o"&gt;))&lt;/span&gt;&lt;span class="se"&gt;\[&lt;/span&gt;0]

ax&lt;span class="se"&gt;\[&lt;/span&gt;0].imshow&lt;span class="o"&gt;(&lt;/span&gt;image.permute&lt;span class="o"&gt;(&lt;/span&gt;1, 2, 0&lt;span class="o"&gt;))&lt;/span&gt;
ax&lt;span class="se"&gt;\[&lt;/span&gt;0].axis&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'off'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
ax&lt;span class="se"&gt;\[&lt;/span&gt;0].set&lt;span class="se"&gt;\_&lt;/span&gt;title&lt;span class="o"&gt;(&lt;/span&gt;f&lt;span class="s1"&gt;'Before - {tuple(image.shape)}'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
ax&lt;span class="se"&gt;\[&lt;/span&gt;1].imshow&lt;span class="o"&gt;(&lt;/span&gt;image&lt;span class="se"&gt;\_&lt;/span&gt;augmented.permute&lt;span class="o"&gt;(&lt;/span&gt;1, 2, 0&lt;span class="o"&gt;))&lt;/span&gt;
ax&lt;span class="se"&gt;\[&lt;/span&gt;1].axis&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'off'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
ax&lt;span class="se"&gt;\[&lt;/span&gt;1].set&lt;span class="se"&gt;\_&lt;/span&gt;title&lt;span class="o"&gt;(&lt;/span&gt;f&lt;span class="s1"&gt;'After - {tuple(image\_augmented.shape)}'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
plt.tight&lt;span class="se"&gt;\_&lt;/span&gt;layout&lt;span class="o"&gt;()&lt;/span&gt;
outputs:
output 0:
output&lt;span class="se"&gt;\_&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;: display&lt;span class="se"&gt;\_&lt;/span&gt;data
data:
image/png: iVBORw0K...&amp;lt;snip &lt;span class="nb"&gt;base64&lt;/span&gt;, &lt;span class="nv"&gt;md5&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;3ea6c670233dfdcd...&amp;gt;
text/plain: &amp;lt;Figure size 720x360 with 2 Axes&amp;gt;
    metadata &lt;span class="o"&gt;(&lt;/span&gt;unknown keys&lt;span class="o"&gt;)&lt;/span&gt;:
    needs_background: light
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This may not be as friendly as the view in the browser, but in a sub-critical situation it’s certainly better than JSON 😉&lt;/p&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://nbdime.readthedocs.io/en/latest/index.html"&gt;Nbdime&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://drivendata.github.io/cookiecutter-data-science/#directory-structure"&gt;Data Science Cookie Cutter&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.sublimemerge.com/"&gt;Sublime Merge&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  TL;DR:
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;nbdime

&lt;span class="nv"&gt;$ &lt;/span&gt;nbdiff notebook&lt;span class="se"&gt;\_&lt;/span&gt;1.ipynb notebook&lt;span class="se"&gt;\_&lt;/span&gt;2.ipynb

&lt;span class="nv"&gt;$ &lt;/span&gt;nbdiff-web &lt;span class="se"&gt;\[&lt;/span&gt;~&amp;lt;commit&amp;gt;~ &lt;span class="se"&gt;\[&lt;/span&gt;~&amp;lt;commit&amp;gt;~]] &lt;span class="se"&gt;\[&lt;/span&gt;~&amp;lt;path&amp;gt;~]

&lt;span class="nv"&gt;$ &lt;/span&gt;nbmerge-web base.ipynb local.ipynb remote.ipynb &lt;span class="nt"&gt;--out&lt;/span&gt; merged.ipynb

&lt;span class="nv"&gt;$ &lt;/span&gt;nbdime config-git &lt;span class="nt"&gt;--enable&lt;/span&gt; &lt;span class="nt"&gt;--global&lt;/span&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;git mergetool &lt;span class="nt"&gt;--tool&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;nbdime biutiful&lt;span class="se"&gt;\_&lt;/span&gt;notebook.ipynb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Originally published at &lt;a href="https://tailored.ml/blog/how-to-merge-jupyter-notebooks"&gt;https://tailored.ml/blog/how-to-merge-jupyter-notebooks&lt;/a&gt;&lt;/p&gt;

</description>
      <category>machinelearning</category>
      <category>jupyter</category>
      <category>datascience</category>
      <category>git</category>
    </item>
  </channel>
</rss>
