import hashlib sha1_hash_function = hashlib.sha1 sha1_hash_function message = "git is a rude word in UK English" hash_value = sha1_hash_function(message).hexdigest() hash_value sha1_hash_function("git is a rude word in UK English.").hexdigest() # Add a full stop # Make the directory for the paper and change into the directory from os import chdir, getcwd, mkdir, listdir from os.path import split as psplit, abspath, isdir import shutil # If we are in the nobel_prize directory already, get out current_path = abspath(getcwd()) if current_path.endswith('nobel_prize'): chdir('..') # Change down a directory # If the directory exists, nuke it if isdir('nobel_prize'): shutil.rmtree('nobel_prize') # Make the new directory, and change to it mkdir('nobel_prize') chdir('nobel_prize') # Check we are there print(getcwd()) # The directory should be empty at this point assert listdir('.') == [] %%file nobel_prize.txt The brain is just a set of random numbers ========================================= We have discovered that the brain is a set of random numbers. We have charts and graphs to back us up. %%file very_clever_analysis.py # The brain analysis script import numpy as np # Make brain data brain_size = (128, 128) random_data = np.random.normal(size=brain_size) # Get the figure from storage !cp ../images/stunning_figure.png . import os os.listdir('.') !mkdir .fancy_backups !mkdir .fancy_backups/1 !mkdir .fancy_backups/1/files !cp * .fancy_backups/1/files """ A little utility to display the structure of directory trees Don't worry about the detail here, you'll see below what it does. The code is not complicated, but it's not relevant to the main points on git. Inspired by: http://lorenzod8n.wordpress.com/2007/12/13/creating-a-tree-utility-in-python-part-2/ with thanks. This version is my own copyright (Matthew Brett) released under 2-clause BSD """ from os import getcwd, listdir from os.path import isdir, join as pjoin # Unicode constants for constructing the tree trunk and branches ALONG = u'\u2500' DOWN = u'\u2502' DOWN_RIGHT = u'\u251c' ELBOW_RIGHT = u'\u2514' BLUE = u'\033[94m' ENDC = u'\033[0m' DOWN_RIGHT_ALONG = DOWN_RIGHT + ALONG * 2 + u" " ELBOW_RIGHT_ALONG = ELBOW_RIGHT + ALONG * 2 + u" " CONTINUE_INDENT = DOWN + u' ' * 3 FINISH_INDENT = u' ' * 4 def print_tree(root_path=None, indent_str=u''): """ Print tree structure starting from `root_path` Parameters ---------- root_path : None or str, optional path from which to print directory tree structure. If None, use current directory. indent_str : str, optional prefix to print for every entry in the tree. Usually '', and then set by recursion into the function when printing subdirectories. Returns ------- None """ if root_path is None: root_path = getcwd() # ensure return of unicode paths from listdir root_path = unicode(root_path) paths = sorted(listdir(root_path)) if len(paths) == 0: return for path in paths[:-1]: print_path(root_path, path, indent_str, False) print_path(root_path, paths[-1], indent_str, True) def print_path(root_path, path, indent_str, last_entry=False): """ Print individual `path` (file or directory name) from `root_path` Parameters ---------- root_path : str path containing `path` path : str file name or directory name indent_str : str string to prefix to entry for this `path` last_entry : bool, optional Whether this is the last entry in a list of paths. Returns ------- None """ full_path = pjoin(root_path, path) have_dir = isdir(full_path) leader = ELBOW_RIGHT_ALONG if last_entry else DOWN_RIGHT_ALONG path_colored = BLUE + path + ENDC if have_dir else path print(indent_str + leader + path_colored) if have_dir: new_indent = FINISH_INDENT if last_entry else CONTINUE_INDENT print_tree(full_path, indent_str + new_indent) %%file .fancy_backups/1/info.txt Date: April 1 2012, 14.30 Author: I. M. Awesome Notes: First backup of my amazing idea print_tree() !echo "The charts are very impressive\n" >> nobel_prize.txt !mkdir .fancy_backups/2 !mkdir .fancy_backups/2/files !cp * .fancy_backups/2/files %%file .fancy_backups/2/info.txt Date: April 1 2012, 18.03 Author: I. M. Awesome Notes: Fruit of enormous thought !echo "The graphs are also compelling\n" >> nobel_prize.txt !mkdir .fancy_backups/3 !mkdir .fancy_backups/3/files !cp * .fancy_backups/3/files %%file .fancy_backups/3/info.txt Date: April 2 2012, 11.20 Author: I. M. Awesome Notes: Now seeing things clearly print_tree() # Now we just cheat, and copy the commits !cp -r .fancy_backups/3 .fancy_backups/4 !cp -r .fancy_backups/3 .fancy_backups/5 !cp -r .fancy_backups/3 .fancy_backups/6 !mkdir .fancy_backups/staging_area !cp .fancy_backups/6/files/* .fancy_backups/staging_area !ls .fancy_backups/staging_area !cp nobel_prize.txt .fancy_backups/staging_area !mkdir .fancy_backups/7 !mkdir .fancy_backups/7/files !cp .fancy_backups/staging_area/* .fancy_backups/7/files %%file .fancy_backups/7/info.txt Date: April 10 2012, 14.30 Author: I. M. Awesome Notes: Changes to introduction !cp very_clever_analysis.py .fancy_backups/staging_area # Our commit procedure; same as last time with "8" instead of "7" !mkdir .fancy_backups/8 !mkdir .fancy_backups/8/files !cp .fancy_backups/staging_area/* .fancy_backups/8/files %%file .fancy_backups/8/info.txt Date: April 10 2012, 14.35 Author: I. M. Awesome Notes: Crazy new analysis print_tree('.fancy_backups') !mkdir .fancy_backups/objects import hashlib # read the bytes for the figure into a string figure_data = open('.fancy_backups/1/files/stunning_figure.png', 'rb').read() # Calculate the hash value figure_hash = hashlib.sha1(figure_data).hexdigest() figure_hash !cp .fancy_backups/1/files/stunning_figure.png .fancy_backups/objects/$figure_hash import os def copy_to_hash(fname): # Read the data binary_data = open(fname, 'rb').read() # Find the hash hash_value = hashlib.sha1(binary_data).hexdigest() # Save with the hash filename hash_fname = '.fancy_backups/objects/' + hash_value if os.path.isfile(hash_fname): print "We already have a hash file for " + fname else: open(hash_fname, 'wb').write(binary_data) return hash_value paper_hash = copy_to_hash('.fancy_backups/1/files/nobel_prize.txt') script_hash = copy_to_hash('.fancy_backups/1/files/very_clever_analysis.py') print 'Hash for stunning_figure.png:', figure_hash print 'Hash for nobel_prize:', paper_hash print 'Hash for very_clever_analysis.py:', script_hash print_tree('.fancy_backups/objects') %%file .fancy_backups/1/directory_list Filename Hash value ======== ========== stunning_figure.png aff88ecead2c7166770969a54dc855c8b91be864 nobel_prize.txt 3af8809ecb9c6dec33fc7e5ad330e384663f5a0d very_clever_analysis.py e7f3ca9157fd7088b6a927a618e18d4bc4712fb6 # Nothing to for unchanged figure - already have hash value file figure_hash = copy_to_hash('.fancy_backups/2/files/stunning_figure.png') # Makes a new copy paper_hash = copy_to_hash('.fancy_backups/2/files/nobel_prize.txt') # Nothing to for unchanged script - already have hash value file script_hash = copy_to_hash('.fancy_backups/2/files/very_clever_analysis.py') print 'Hash for stunning_figure.png:', figure_hash print 'Hash for nobel_prize:', paper_hash print 'Hash for very_clever_analysis.py:', script_hash %%file .fancy_backups/2/directory_list Filename Hash value ======== ========== stunning_figure.png aff88ecead2c7166770969a54dc855c8b91be864 nobel_prize.txt bc330d8886bb1b36a49d8e7ebb07d3443190b0e6 very_clever_analysis.py e7f3ca9157fd7088b6a927a618e18d4bc4712fb6 tree_hash = copy_to_hash('.fancy_backups/1/directory_list') tree_hash copy_to_hash('.fancy_backups/2/directory_list') %%file .fancy_backups/1/commit Date: April 1 2012, 14.30 Author: I. M. Awesome Notes: First backup of my amazing idea Tree: 63d466086dd359c0b34d21e04b812781c7153b23 commit_hash = copy_to_hash('.fancy_backups/1/commit') commit_hash ls .fancy_backups/objects !cat .fancy_backups/objects/f473e51a38d40772722205c92dddd4bdfd941ef8 %%file .fancy_backups/temporary_file Date: April 1 2012, 18.03 Author: I. M. Awesome Notes: Fruit of enormous thought Tree: d45095659d1ea567b90aaf923e265bf41cbb126f Parent: 63dab6e48ed2e9111624a3b5391bdf784c040b86 commit2_hash = copy_to_hash('.fancy_backups/temporary_file') !git config --global user.name "Matthew Brett" !git config --global user.email "matthew.brett@gmail.com" # Put here your preferred editor. !git config --global core.editor gedit !git config --global color.ui "auto" !git # First pull the contents of the first commit from .fancy_backups !cp .fancy_backups/1/files/* . # Then delete .fancy_backups - we're done with those guys !rm -rf .fancy_backups !ls -a !git init !ls .git print_tree('.git/objects') !git add nobel_prize.txt print_tree('.git/objects') !git status import zlib # A compression / decompression library def read_git_object(filename): """ Read an object in the ``.git/objects`` directory Yes, it's this simple - in this case. But git reserves the right to use more complicated storage formats to save space when your repository is larger """ compressed_contents = open(filename, 'rb').read() return zlib.decompress(compressed_contents) filename = '.git/objects/d9/2d079af6a7f276cc8d63dcf2549c03e7deb553' new_object_data = read_git_object(filename) print new_object_data !git add stunning_figure.png !git add very_clever_analysis.py !git status print_tree('.git/objects') print read_git_object('.git/objects/8a/dc8bf371d669242ea998f78f9916867cc6203c') !git commit -m "First backup of my amazing idea" print_tree('.git/objects') print read_git_object('.git/objects/1e/322f4e782df4dfe963eda96517886f4e39a454') !git ls-tree 1e322f4 !git log !git log --parents !git branch !git branch -v !ls .git/refs/heads !cat .git/refs/heads/master !echo "\nThe charts are very impressive\n" >> nobel_prize.txt !git diff !git status !git commit !git status !git add nobel_prize.txt !git status !git commit -m "Fruit of enormous thought" !cat .git/refs/heads/master !git log !git log --parents !git log --oneline --topo-order --graph # We create our alias (this saves it in git's permanent configuration file): !git config --global alias.slog "log --oneline --topo-order --graph" # And now we can use it !git slog !git mv very_clever_analysis.py slightly_dodgy_analysis.py !git status !git commit -m "I like this new name better" !git slog !cat .git/HEAD !cat .git/refs/heads/master !git branch -v !git status !ls !git branch experiment !ls .git/refs/heads !git branch -v !cat .git/refs/heads/experiment !git checkout experiment !git branch -v !cat .git/HEAD !echo "Some crazy idea" > experiment.txt !git add experiment.txt !git commit -m "Trying something new" !git slog !ls !cat .git/refs/heads/experiment !git checkout master !cat .git/HEAD !ls !git slog !echo "All the while, more work goes on in master..." >> nobel_prize.txt !git add nobel_prize.txt !git commit -m "The mainline keeps moving" !git slog !ls !git merge experiment -m "Merge in the experiment" !git slog !git branch trouble !git checkout trouble !echo "This is going to be a problem..." >> experiment.txt !git add experiment.txt !git commit -m"Changes in the trouble branch" !git checkout master !echo "More work on the master branch..." >> experiment.txt !git add experiment.txt !git commit -m"Mainline work" !git config merge.conflictstyle diff3 !git merge trouble -m "Unlikely this one will work" !cat experiment.txt %%file experiment.txt Some crazy idea More work on the master branch... This is going to be a problem... !git status !git add experiment.txt !git commit -m "Completed merge of trouble, fixing conflicts along the way" !git slog