{"id":14977193,"url":"https://github.com/brookisme/gitnb","last_synced_at":"2025-10-28T01:30:35.952Z","repository":{"id":57434746,"uuid":"92433674","full_name":"brookisme/gitnb","owner":"brookisme","description":"git tracking for python notebooks","archived":false,"fork":false,"pushed_at":"2017-06-15T13:55:21.000Z","size":332,"stargazers_count":12,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-02-01T10:23:35.876Z","etag":null,"topics":["git","ipynb","ipython","ipython-notebook","jupyter-notebook","python"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/brookisme.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-05-25T18:43:24.000Z","updated_at":"2021-11-14T21:54:19.000Z","dependencies_parsed_at":"2022-09-04T15:24:35.010Z","dependency_job_id":null,"html_url":"https://github.com/brookisme/gitnb","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brookisme%2Fgitnb","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brookisme%2Fgitnb/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brookisme%2Fgitnb/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brookisme%2Fgitnb/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/brookisme","download_url":"https://codeload.github.com/brookisme/gitnb/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":238579176,"owners_count":19495510,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["git","ipynb","ipython","ipython-notebook","jupyter-notebook","python"],"created_at":"2024-09-24T13:55:16.259Z","updated_at":"2025-10-28T01:30:35.593Z","avatar_url":"https://github.com/brookisme.png","language":"Python","readme":"## GITNB \n\n**GIT TRACKING FOR PYTHON NOTEBOOKS**\n\nA simple idea: GitNB doesn't actually track python notebooks. Instead, GitNB creates and updates python versions of your notebooks which are in turn tracked by git.\n\n1. [Quick Start](#quick)\n2. [But I'm Lazy!!!](#lazy)\n3. [Install](#install)\n4. [Docs](#docs)\n5. [User Config](#config)\n\n_____\n\u003ca name='quick'\u003e\u003c/a\u003e\n### QUICK START:\n\nThis quick-start is just an example. It looks long (due to bash-output) but its quick: 1-2 minutes tops.\n\nA. INITIALIZE GIT REPO\n\n```bash\ntest| $ tree\n.\n├── A-Notebook.ipynb\n├── A_BUGGY_NOTEBOOK.ipynb\n├── Py2NB.ipynb\n├── another_python_file.py\n├── some_python_file.py\n└── widget\n    ├── I\\ have\\ spaces\\ in\\ my\\ name.ipynb\n    ├── Notebook1.ipynb\n    └── widget.py\n\n1 directory, 8 files\n\ntest| $ git init\nInitialized empty Git repository in /Users/brook/code/jupyter/gitnb/test/.git/\n\ntest| $ git add .\n\ntest| $ git commit -am \"Initial Commit: python files\"\n[master (root-commit) b29b6c4] ...\n```\n\n\nB. INITIALIZE GitNB, ADD NOTEBOOKS TO GitNB TO BE TRACKED\n\n```bash\n\n# initialize gitnb\ntest|master $ gitnb init\n\ngitnb: INSTALLED \n   - nbpy.py files will be created/updated/tracked\n   - install user config with: $ gitnb configure\n\n# lets list our (untracked) notebooks\ntest|master $ gitnb list\n\ngitnb[untracked]:\n  Py2NB.ipynb\n  A-Notebook.ipynb\n  widget/I have spaces in my name.ipynb\n  A_BUGGY_NOTEBOOK.ipynb\n  widget/Notebook1.ipynb\n\n# adding an individual file\ntest|master $ gitnb add A_BUGGY_NOTEBOOK.ipynb \ngitnb: add (A_BUGGY_NOTEBOOK.ipynb | nbpy/A_BUGGY_NOTEBOOK.nbpy.py)\n\n# adding all the files in a directory\ntest|master $ gitnb add widget\ngitnb: add (widget/I have spaces in my name.ipynb | nbpy/I have spaces in my name.nbpy.py)\ngitnb: add (widget/Notebook1.ipynb | nbpy/Notebook1.nbpy.py)\n\n# the default directory for the python versions of the notebooks is nbpy/\ntest|master $ tree\n.\n├── A-Notebook.ipynb\n├── A_BUGGY_NOTEBOOK.ipynb\n├── Py2NB.ipynb\n├── another_python_file.py\n├── nbpy\n│   ├── A_BUGGY_NOTEBOOK.nbpy.py\n│   ├── I\\ have\\ spaces\\ in\\ my\\ name.nbpy.py\n│   └── Notebook1.nbpy.py\n├── some_python_file.py\n└── widget\n    ├── I\\ have\\ spaces\\ in\\ my\\ name.ipynb\n    ├── Notebook1.ipynb\n    └── widget.py\n\n2 directories, 11 files\n\n# our list now conatins tracked and untracked notebooks\ntest|master $ gitnb list\n\ngitnb[tracked]:\n  widget/Notebook1.ipynb\n  widget/I have spaces in my name.ipynb\n  A_BUGGY_NOTEBOOK.ipynb\n\ngitnb[untracked]:\n  A-Notebook.ipynb\n  Py2NB.ipynb\n\n# note these files are now in our git repo\ntest|master $ git status\nOn branch master\nChanges to be committed:\n  (use \"git reset HEAD \u003cfile\u003e...\" to unstage)\n\n  new file:   nbpy/A_BUGGY_NOTEBOOK.nbpy.py\n  new file:   nbpy/I have spaces in my name.nbpy.py\n  new file:   nbpy/Notebook1.nbpy.py\n\n# git commit the new nbpy.py versions\ntest|master $ git commit -am \"add nbpy.py versions of notebooks\"\n[master 868b0a2] ...\n```\n\n\nC. QUICK LOOK AT A \"NBPY.PY\" VERSION OF A NOTEBOOK\n\n\n```bash\ntest|master $ cat nbpy/A_BUGGY_NOTEBOOK.nbpy.py \n\n\n\"\"\"[markdown]\n## This is a notebook with bugs\n\"\"\"\n\n\n\"\"\"[code]\"\"\"\nimport numpy as np\n\"\"\"\"\"\"\n\n\n\"\"\"[code]\"\"\"\ndef feature(food=True):\n    if foo:\n        return \"I am not a bug\"\n    else:\n        return \"I told you I am not a bug\"\n\"\"\"\"\"\"\n\n\n\"\"\"[code]\"\"\"\nprint(\"Are you a bug?\")\nprint(feature(True))\n\"\"\"\"\"\"\n\n```\n\nD. UPDATE NBPY.PY FILE AFTER EDITING YOUR NOTEBOOK\n\nThat notebook is buggy ...[updating python notebook]... I just went to the python-notebook and fixed the bugs. Let's see what happened:\n\n```bash\n# note the changes have not appeared in our nbpy.py file\ntest|master $ git diff\n\n# however, we can see the changes with 'gitnb diff'\ntest|master $ gitnb diff A_BUGGY_NOTEBOOK.ipynb\n\ngitnb[diff]: A_BUGGY_NOTEBOOK.ipynb[-\u003enbpy.py] - nbpy/A_BUGGY_NOTEBOOK.nbpy.py\n--- +++ @@ -1,7 +1,7 @@ \n \n \"\"\"[markdown]\n-## This is a notebook with bugs\n+## This is a notebook without bugs\n \"\"\"\n \n \n@@ -11,7 +11,7 @@ \n \n \"\"\"[code]\"\"\"\n-def feature(food=True):\n+def feature(foo=True):\n     if foo:\n         return \"I am not a bug\"\n     else:\n\n\n# we now use 'gitnb update' to update the tracked files\n# this creates a new nbpy.py version and adds the changes\n# to the git repo\ntest|master $ gitnb update\n\n# now we can see the bug fixes with 'git diff'\ntest|master $ git diff\ndiff --git a/nbpy/A_BUGGY_NOTEBOOK.nbpy.py b/nbpy/A_BUGGY_NOTEBOOK.nbpy.py\nindex e80204b..955b359 100644\n--- a/nbpy/A_BUGGY_NOTEBOOK.nbpy.py\n+++ b/nbpy/A_BUGGY_NOTEBOOK.nbpy.py\n@@ -1,7 +1,7 @@\n \n \n \"\"\"[markdown]\n-## This is a notebook with bugs\n+## This is a notebook without bugs\n \"\"\"\n \n \n@@ -11,7 +11,7 @@ import numpy as np\n \n \n \"\"\"[code]\"\"\"\n-def feature(food=True):\n+def feature(foo=True):\n     if foo:\n         return \"I am not a bug\"\n     else:\n\n# commit the changes\ntest|master $ git commit -am \"fixed bug: i fixed .ipynb, gitnb fixed .nbpy.py\"\n[master 812a4f0] ...\n```\n\nE. CREATE PYTHON-NOTEBOOK FROM NBPY.PY FILE\n\nFinally, lets say we actually need that buggy notebook after all\n\n```bash\ntest|master $ git checkout 868b0a2\nNote: checking out '868b0a2'.\n[... git detached head messaging ...]\nHEAD is now at 868b0a2... add nbpy.py versions of notebooks\n\n# create notebook from nbpy.py file\ntest|(HEAD detached at 868b0a2) $ gitnb tonb nbpy/A_BUGGY_NOTEBOOK.nbpy.py \n\n# the default directory for generated notebooks versions is nbpy/\ntest|(HEAD detached at 868b0a2) $ tree nbpy_nb\nnbpy_nb\n└── A_BUGGY_NOTEBOOK.nbpy.ipynb\n\n0 directories, 1 file\n```\n\nMy bugs are back!\n\n![nbpy_nb/A_BUGGY_NOTEBOOK.nbpy.ipynb](https://github.com/brookisme/gitnb/blob/master/buggy.png)\n\n_____\n\u003ca name='lazy'\u003e\u003c/a\u003e\n### LAZY CONFIG:\n\nIf the [quick-start](#quick) seemed like too much how about this...\n\n```bash\n$ gitnb commit -am \"I just updated and commited every notebook in my project\" \n```\n\nHow in the what? Two things are going on here\n\n1. We are [commit](#commit)-ing with `gitnb commit` instead of `git commit`\n2. I've [installed](#configure) the user config and set\n\n```bash\n# ./gitnb.config.yaml\n...\nGIT_ADD_ON_GITNB_UPDATE: True\nAUTO_TRACK_ALL_NOTEBOOKS: True\n...\n```\n\nNow each time I `gitnb commit`:\n\n* All new notebooks are [add](#add)-ed to be tracked by gitnb\n* All notebooks are [update](#update)-ed\n* All changes are added to the git repo\n* `git commit --allow-empty` is [called](#commit)\n\nNote: the `--allow-empty` flag is there because the at the time of the commit (before the nbpy.py files are generated there may or may not be changes to commit)\n\nHere's the super-quick-quick-start-example:\n```bash\ntest|master $ git init\nInitialized empty Git repository in /Users/brook/code/jupyter/gitnb/test/.git/\ntest| $ gitnb init\n\ngitnb: INSTALLED \n   - nbpy.py files will be created/updated/tracked\n   - install user config with: $ gitnb configure\n\ntest| $ gitnb configure\ngitnb: USER CONFIG FILE ADDED (./gitnb.config.yaml) \n```\n\n... go update gitnb.config.yaml ...\n\n```bash\ntest| $ gitnb commit -am \"Initial Commit with everything\"\ngitnb: add (A-Notebook.ipynb | nbpy/A-Notebook.nbpy.py)\ngitnb: add (A_BUGGY_NOTEBOOK.ipynb | nbpy/A_BUGGY_NOTEBOOK.nbpy.py)\ngitnb: add (Py2NB.ipynb | nbpy/Py2NB.nbpy.py)\ngitnb: add (widget/I have spaces in my name.ipynb | nbpy/I have spaces in my name.nbpy.py)\ngitnb: add (widget/Notebook1.ipynb | nbpy/Notebook1.nbpy.py)\n[master (root-commit) cb8c106] ...\n\ntest|master $ tree\n.\n├── A-Notebook.ipynb\n├── A_BUGGY_NOTEBOOK.ipynb\n├── Py2NB.ipynb\n├── another_python_file.py\n├── gitnb.config.yaml\n├── nbpy\n│   ├── A-Notebook.nbpy.py\n│   ├── A_BUGGY_NOTEBOOK.nbpy.py\n│   ├── I\\ have\\ spaces\\ in\\ my\\ name.nbpy.py\n│   ├── Notebook1.nbpy.py\n│   └── Py2NB.nbpy.py\n├── some_python_file.py\n└── widget\n    ├── I\\ have\\ spaces\\ in\\ my\\ name.ipynb\n    ├── Notebook1.ipynb\n    └── widget.py\n\n2 directories, 14 files\n```\n_____\n\u003ca name='install'\u003e\u003c/a\u003e\n### INSTALL:\n\n###### pip:\n```bash\npip install gitnb\n```\n\n###### github:\n```\ngit clone https://github.com/brookisme/gitnb.git\ncd gitnb\nsudo pip install -e .\n```\n\n_____\n\u003ca name='docs'\u003e\u003c/a\u003e\n### DOCS:\n\n```bash\n$ gitnb --help\nusage: gitnb [-h] {init,configure,list,update,add,remove,topy,tonb} ...\n```\n\n\u003ca name='methods'\u003e\u003c/a\u003e\n###### methods:\n\n1. [init](#init): initialize gitnb for project\n2. [configure](#configure): install gitnb.config.yaml for user config\n3. [gitignore](#gitignore): add ipynb \\\u0026 gitnb files to gitignore\n4. [list](#list): list tracked notebooks or nbpy.py files\n5. [add](#add): begin tracking notebook\n6. [remove](#remove): stop tracking notebook\n7. [update](#update): update nbpy.py files with recent notebook edits\n8. [commit](#commit): update, followed by add, followed by `git commit`\n9. [diff](#diff): perform diff between current notebook version and last [update](#update)-ed version\n10. [topy](#topy): convert notebook to nbpy.py file (without [add](#add)-ing)\n11. [tonb](#tonb): convert nbpy.py file to python notebook\n\n_____\n\u003ca name='init'\u003e\u003c/a\u003e\n\n###### init:\nInitialize Project:\n\n* `git init` required before `gitnb init`\n* installs .gitnb directory at the project root\n* creates or appends .git/hooks/pre-commit for auto-tracking config\n\n```bash\n$ gitnb init\n```\n([back to methods](#methods))\n\n_____\n\u003ca name='configure'\u003e\u003c/a\u003e\n###### configure:\nInstall Config:\n\n* optional: only necesary if you want to change the [default config](https://github.com/brookisme/gitnb/blob/master/gitnb/default.config.yaml)\n* installs gitnb.config.yaml directory at the project root\n\n```bash\n$ gitnb configure\n```\n([back to methods](#methods))\n\n_____\n\u003ca name='gitignore'\u003e\u003c/a\u003e\n###### gitignore:\nUpdate .gitignore:\n\n\n\nAppends (or creates) gitignore with the recommended settings. Namely,\n\n* notebooks: *.ipynb, .ipynb_checkpoints\n* gitnb: nbpy_nb/\n\n```bash\n$ gitnb gitignore\n```\n([back to methods](#methods))\n\n_____\n\u003ca name='list'\u003e\u003c/a\u003e\n###### list:\nList Project Notebooks, or nbpy.py files\n\npositional arg (*type*):\n\n* (default) **all**: list tracked and untracked notebooks\n* **tracked**: list tracked notebooks\n* **untracked**: list untracked notebooks\n* **nbpy**: list nbpy.py files\n\n```bash\n$ gitnb list --help\nusage: gitnb list [-h] [type]\n\npositional arguments:\n  type        notebooks: ( all | tracked | untracked ), or nbpy\n```\n([back to methods](#methods))\n\n_____\n\u003ca name='add'\u003e\u003c/a\u003e\n###### add:\nAdd notebook to gitnb:\n\n* converts notebook(s) to nbpy.py file(s)\n* adds notebook-nbpy pair to gitnb tracking list\n* performs a `git add` on nbpy.py file(s)\n* path: path to file or directory \n* destination_path: (optional) \n    * if path is a file path nbpy file will be at destination_path\n    * if destination_path is falsey (recommended) default path is used\n    * default path can be changed with [user config](#config)\n    * if path is a direcotry path, default config is always used\n\n```bash\n$ gitnb add --help\nusage: gitnb add [-h] path [destination_path]\n\npositional arguments:\n  path              path to ipynb file\n  destination_path  if falsey uses default destination path\n```\n([back to methods](#methods))\n\n_____\n\u003ca name='remove'\u003e\u003c/a\u003e\n###### remove:\nRemove notebook from gitnb:\n\n* notebook will no longer be tracked\n* nbpy.py file will **not** be deleted\n* ipynb file will **not** be deleted\n\n```bash\n$ gitnb remove --help\nusage: gitnb remove [-h] path\n\npositional arguments:\n  path        path to ipynb file\n```\n([back to methods](#methods))\n\n_____\n\u003ca name='update'\u003e\u003c/a\u003e\n###### update:\nUpdate nbpy files:\n\n* will update nbpy files with current content from your tracked notebooks\n* make sure your notebook has been saved!\n\n```bash\n$ gitnb update\n```\n([back to methods](#methods))\n\n_____\n\u003ca name='commit'\u003e\u003c/a\u003e\n###### commit:\nUpdate and Commit:\n\n* if (UPDATE_ON_GITNB_COMMIT) perform 'gitnb update'\n* call `git commit --allow-empty` with optional flags [a|m]:\n  - -a flag (add all - same as git commit -a)\n  - -m flag (add all - same as git commit -m)\n  - note: `--allow-empty` because the nbpy files are not yet updated\n\n```bash\n#\n# this line of code is equivalent to\n# - $ gitnb update\n# - $ git add .\n# - $ git commit --allow-empty -am \"COMMIT MESSAGE\"\n#\n$ gitnb commit [-a] [-m \"COMMIT MESSAGE\"]\n```\n([back to methods](#methods))\n\n_____\n\u003ca name='diff'\u003e\u003c/a\u003e\n###### diff:\nDiff for recent changes.\n\nCreates a diff between the most recent nbpy.py version of the noteboook\nand the nbpy.py version of the notebook in its current state (the working copy).\n\n```bash\n$ gitnb diff \u003cPATH-TO-NOTEBOOK(.ipynb)-FILE\u003e\n```\n([back to methods](#methods))\n\n_____\n\u003ca name='topy'\u003e\u003c/a\u003e\n###### topy:\nTo-Python:\n\n* converts notebook(s) to nbpy.py file(s)\n* similar to [add](#add) but does not gitnb or git track\n\n```bash\n$ gitnb topy --help\nusage: gitnb topy [-h] path [destination_path]\n\npositional arguments:\n  path              path to ipynb file\n  destination_path  if falsey uses default destination path\n```\n([back to methods](#methods))\n\n_____\n\u003ca name='tonb'\u003e\u003c/a\u003e\n###### tonb:\nTo-Notebook:\n\n* creates new notebook from nbpy.py python file\n* great for collaborators!\n* great for recovering lost work!\n\n```bash\n$ gitnb tonb --help\nusage: gitnb tonb [-h] path [destination_path]\n\npositional arguments:\n  path              path to ipynb file\n  destination_path  if falsey uses default destination path\n```\n([back to methods](#methods))\n\n\n_____\n\u003ca name='config'\u003e\u003c/a\u003e\n### USER CONFIG:\n\nThe [configure](#configure) method installs `gitnb.config.yaml` in your root directory.  This is a copy of the [default config](https://github.com/brookisme/gitnb/blob/master/gitnb/default.config.yaml). Note at anytime you can go back to the default configuration by simply deleting the user config file (`gitnb.config.yaml`).\n\nThere are comment-docs in the config file that should explain what each configuration control. However I thought I'd touch a couple of the perhaps more interesting configurations here.\n\n##### LAZY INSTALL\n\nsee [But I'm Lazy!](#lazy)\n\n##### GIT_ADD_ON_GITNB_ADD (defaults to True):\n\nIf True the [add](#add) method will perform a `git add` after creating the nbpy file and adding it to the gitnb tracking list.  You can set this to False if you want to explicity call `git add` yourself after looking over the file.\n\n##### UPDATE_ON_COMMIT (defaults to True):\n\nIf True, the gitnb [update](#update) method will automatically be called when performing a `git commit` (during pre-commit hook).\n\n##### AUTO_TRACK_ALL_NOTEBOOKS (defaults to False):\n\nIf True, `gitnb add .` (see [add](#add) method) will automatically be called when performing a `git commit` (during pre-commit hook). This will add all notebooks in your project to gitnb.\n\n_Note if the only thing that has changed is your notebooks, you'll still need to explicity call `gitnb update` or add the `--allow-empty` flag to your `git commit`._\n\n##### EXCLUDE_DIRS:\n\nA list of directories not to include when searching for notebooks\n\n##### OTHER:\n\nYou can also configure, default location for new files, if they include an indentifier (like 'nbpy' in `somefile.nbpy.py`), spacing in nbpy files and more. Check the comment-docs for more info.\n\n\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbrookisme%2Fgitnb","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbrookisme%2Fgitnb","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbrookisme%2Fgitnb/lists"}