"
]
},
"execution_count": 1,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"import git \n",
"\n",
"GIT_REPO_PATH = r'../../spring-petclinic/'\n",
"repo = git.Repo(GIT_REPO_PATH)\n",
"git_bin = repo.git\n",
"git_bin"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"With the git_bin, we can execute almost any Git command we like directly. In our hypothetical use case, we want to retrieve some information about the change frequency of files. For this, we need the complete history of the Git repo including statistics for the changed files (via --numstat).\n",
"\n",
"We use a little trick to make sure, that the format for the file's statistics fits nicely with the commit's metadata (SHA %h, UNIX timestamp %at and author's name %aN). The --numstat option provides data for additions and deletions for the affected file name in one line – separated by the tabulator character \\t: \n",
"\n",
"1\\t1\\tsome/file/name.ext\n",
"
\n",
"\n",
"We use the same tabular separator \\t for the format string:\n",
"\n",
"%h\\t%at\\t%aN\n",
"
\n",
"\n",
"And here is the trick: Additionally, we add the number of tabulators of the file's statistics plus an additional tabulator in front of the format string to pretend that there is an empty file statistics' information in front of each commit meta data string.\n",
"\n",
"The results looks like this:\n",
"\n",
"\n",
"\\t\\t\\t%h\\t%at\\t%aN\n",
"
\n",
"\n",
"Note: If you want to export the Git log on the command line into a file, you need to use the horizontal tab %x0A as separator instead of \\t in the format string. Otherwise, the trick doesn't work (I'll show the corresponding format string at the end of this article).\n",
"\n",
"\n",
"OK, let's executed the Git log export:"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'\\t\\t\\t101c9dc\\t1498817227\\tDave Syer\\n2\\t3\\tpom.xml\\n\\n\\t\\t\\tffa967c\\t1492026060\\tAntoine Rey\\n1'"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"git_log = git_bin.execute('git log --numstat --pretty=format:\"\\t\\t\\t%h\\t%at\\t%aN\"')\n",
"git_log[:80]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Reading the Git log\n",
"We now read in the complete files' history in the git_log variable. Don't let confuse you by all the \\t characters. \n",
"\n",
"Let's read the result into a Pandas DataFrame by using the read_csv method. Because we can't provide a file path to a CSV data, we have to use StringIO to read in our in-memory buffered content.\n",
"\n",
"Pandas will read the first line of the tabular-separated \"file\", sees the many tabular-separated columns and parses all other lines in the same format / column layout. Additionally, we set the header to None because we don't have one and provide nice names for all the columns that we read in."
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
"
\n",
" \n",
" \n",
" | \n",
" additions | \n",
" deletions | \n",
" filename | \n",
" sha | \n",
" timestamp | \n",
" author | \n",
"
\n",
" \n",
" \n",
" \n",
" 0 | \n",
" NaN | \n",
" NaN | \n",
" NaN | \n",
" 101c9dc | \n",
" 1.498817e+09 | \n",
" Dave Syer | \n",
"
\n",
" \n",
" 1 | \n",
" 2 | \n",
" 3 | \n",
" pom.xml | \n",
" NaN | \n",
" NaN | \n",
" NaN | \n",
"
\n",
" \n",
" 2 | \n",
" NaN | \n",
" NaN | \n",
" NaN | \n",
" ffa967c | \n",
" 1.492026e+09 | \n",
" Antoine Rey | \n",
"
\n",
" \n",
" 3 | \n",
" 1 | \n",
" 1 | \n",
" readme.md | \n",
" NaN | \n",
" NaN | \n",
" NaN | \n",
"
\n",
" \n",
" 4 | \n",
" NaN | \n",
" NaN | \n",
" NaN | \n",
" fd1c742 | \n",
" 1.488785e+09 | \n",
" Antoine Rey | \n",
"
\n",
" \n",
"
\n",
"
"
],
"text/plain": [
" additions deletions filename sha timestamp author\n",
"0 NaN NaN NaN 101c9dc 1.498817e+09 Dave Syer\n",
"1 2 3 pom.xml NaN NaN NaN\n",
"2 NaN NaN NaN ffa967c 1.492026e+09 Antoine Rey\n",
"3 1 1 readme.md NaN NaN NaN\n",
"4 NaN NaN NaN fd1c742 1.488785e+09 Antoine Rey"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"import pandas as pd\n",
"from io import StringIO\n",
"\n",
"commits_raw = pd.read_csv(StringIO(git_log), \n",
" sep=\"\\t\",\n",
" header=None, \n",
" names=['additions', 'deletions', 'filename', 'sha', 'timestamp', 'author']\n",
" )\n",
"commits_raw.head()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now we have two different kinds of content for the rows:\n",
"- The commit meta data without file statistics (see rows with the indexes 0, 2 and 4 above)\n",
"- The file statistics without the commit meta data (see rows with the indexes 1 and 3 above)\n",
"\n",
"But we are interested in the commit meta data for each file's statistic. For this, we forward fill (ffill) the empty commit meta data entries of the file statistics rows with the preceding commit's meta data via the DataFrame's fillna method and join this data with the existing columns of the file statistics."
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
"
\n",
" \n",
" \n",
" | \n",
" additions | \n",
" deletions | \n",
" filename | \n",
" sha | \n",
" timestamp | \n",
" author | \n",
"
\n",
" \n",
" \n",
" \n",
" 0 | \n",
" NaN | \n",
" NaN | \n",
" NaN | \n",
" 101c9dc | \n",
" 1.498817e+09 | \n",
" Dave Syer | \n",
"
\n",
" \n",
" 1 | \n",
" 2 | \n",
" 3 | \n",
" pom.xml | \n",
" 101c9dc | \n",
" 1.498817e+09 | \n",
" Dave Syer | \n",
"
\n",
" \n",
" 2 | \n",
" NaN | \n",
" NaN | \n",
" NaN | \n",
" ffa967c | \n",
" 1.492026e+09 | \n",
" Antoine Rey | \n",
"
\n",
" \n",
" 3 | \n",
" 1 | \n",
" 1 | \n",
" readme.md | \n",
" ffa967c | \n",
" 1.492026e+09 | \n",
" Antoine Rey | \n",
"
\n",
" \n",
" 4 | \n",
" NaN | \n",
" NaN | \n",
" NaN | \n",
" fd1c742 | \n",
" 1.488785e+09 | \n",
" Antoine Rey | \n",
"
\n",
" \n",
"
\n",
"
"
],
"text/plain": [
" additions deletions filename sha timestamp author\n",
"0 NaN NaN NaN 101c9dc 1.498817e+09 Dave Syer\n",
"1 2 3 pom.xml 101c9dc 1.498817e+09 Dave Syer\n",
"2 NaN NaN NaN ffa967c 1.492026e+09 Antoine Rey\n",
"3 1 1 readme.md ffa967c 1.492026e+09 Antoine Rey\n",
"4 NaN NaN NaN fd1c742 1.488785e+09 Antoine Rey"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"commits = commits_raw[['additions', 'deletions', 'filename']]\\\n",
" .join(commits_raw[['sha', 'timestamp', 'author']].fillna(method='ffill'))\n",
"commits.head()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"This gives use the commit meta data for each file change!\n",
"\n",
"Because we aren't interested in the pure commit meta data anymore, we drop all those rows that don't contain file statistics aka contain null values via dropna."
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
"
\n",
" \n",
" \n",
" | \n",
" additions | \n",
" deletions | \n",
" filename | \n",
" sha | \n",
" timestamp | \n",
" author | \n",
"
\n",
" \n",
" \n",
" \n",
" 1 | \n",
" 2 | \n",
" 3 | \n",
" pom.xml | \n",
" 101c9dc | \n",
" 1.498817e+09 | \n",
" Dave Syer | \n",
"
\n",
" \n",
" 3 | \n",
" 1 | \n",
" 1 | \n",
" readme.md | \n",
" ffa967c | \n",
" 1.492026e+09 | \n",
" Antoine Rey | \n",
"
\n",
" \n",
" 5 | \n",
" 1 | \n",
" 0 | \n",
" pom.xml | \n",
" fd1c742 | \n",
" 1.488785e+09 | \n",
" Antoine Rey | \n",
"
\n",
" \n",
" 8 | \n",
" 1 | \n",
" 1 | \n",
" pom.xml | \n",
" 75912a0 | \n",
" 1.487331e+09 | \n",
" Stephane Nicoll | \n",
"
\n",
" \n",
" 9 | \n",
" 11 | \n",
" 9 | \n",
" src/main/java/org/springframework/samples/petc... | \n",
" 75912a0 | \n",
" 1.487331e+09 | \n",
" Stephane Nicoll | \n",
"
\n",
" \n",
"
\n",
"
"
],
"text/plain": [
" additions deletions filename \\\n",
"1 2 3 pom.xml \n",
"3 1 1 readme.md \n",
"5 1 0 pom.xml \n",
"8 1 1 pom.xml \n",
"9 11 9 src/main/java/org/springframework/samples/petc... \n",
"\n",
" sha timestamp author \n",
"1 101c9dc 1.498817e+09 Dave Syer \n",
"3 ffa967c 1.492026e+09 Antoine Rey \n",
"5 fd1c742 1.488785e+09 Antoine Rey \n",
"8 75912a0 1.487331e+09 Stephane Nicoll \n",
"9 75912a0 1.487331e+09 Stephane Nicoll "
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"commits = commits.dropna()\n",
"commits.head()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"And that's it! We are finished!\n",
"\n",
"In summary, you just need a \"one-liner\" for converting the Git log file output that was exported with\n",
"```\n",
"git log --numstat --pretty=format:\"%x09%x09%x09%h%x09%at%x09%aN\" > git.log\n",
"```\n",
"and read into a DataFrame:"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
"
\n",
" \n",
" \n",
" | \n",
" additions | \n",
" deletions | \n",
" filename | \n",
" sha | \n",
" timestamp | \n",
" author | \n",
"
\n",
" \n",
" \n",
" \n",
" 1 | \n",
" 2 | \n",
" 3 | \n",
" pom.xml | \n",
" 101c9dc | \n",
" 1.498817e+09 | \n",
" Dave Syer | \n",
"
\n",
" \n",
" 3 | \n",
" 1 | \n",
" 1 | \n",
" readme.md | \n",
" ffa967c | \n",
" 1.492026e+09 | \n",
" Antoine Rey | \n",
"
\n",
" \n",
" 5 | \n",
" 1 | \n",
" 0 | \n",
" pom.xml | \n",
" fd1c742 | \n",
" 1.488785e+09 | \n",
" Antoine Rey | \n",
"
\n",
" \n",
" 8 | \n",
" 1 | \n",
" 1 | \n",
" pom.xml | \n",
" 75912a0 | \n",
" 1.487331e+09 | \n",
" Stephane Nicoll | \n",
"
\n",
" \n",
" 9 | \n",
" 11 | \n",
" 9 | \n",
" src/main/java/org/springframework/samples/petc... | \n",
" 75912a0 | \n",
" 1.487331e+09 | \n",
" Stephane Nicoll | \n",
"
\n",
" \n",
"
\n",
"
"
],
"text/plain": [
" additions deletions filename \\\n",
"1 2 3 pom.xml \n",
"3 1 1 readme.md \n",
"5 1 0 pom.xml \n",
"8 1 1 pom.xml \n",
"9 11 9 src/main/java/org/springframework/samples/petc... \n",
"\n",
" sha timestamp author \n",
"1 101c9dc 1.498817e+09 Dave Syer \n",
"3 ffa967c 1.492026e+09 Antoine Rey \n",
"5 fd1c742 1.488785e+09 Antoine Rey \n",
"8 75912a0 1.487331e+09 Stephane Nicoll \n",
"9 75912a0 1.487331e+09 Stephane Nicoll "
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# reading\n",
"git_log = pd.read_csv(\n",
" \"../../spring-petclinic/git.log\",\n",
" sep=\"\\t\", \n",
" header=None,\n",
" names=[\n",
" 'additions', \n",
" 'deletions', \n",
" 'filename', \n",
" 'sha', \n",
" 'timestamp', \n",
" 'author'])\n",
"\n",
"# converting in \"one line\"\n",
"git_log[['additions', 'deletions', 'filename']]\\\n",
" .join(git_log[['sha', 'timestamp', 'author']]\\\n",
" .fillna(method='ffill'))\\\n",
" .dropna().head()"
]
},
{
"cell_type": "markdown",
"metadata": {
"collapsed": true,
"nbpresent": {
"id": "417386b7-669e-4c2e-93bc-2abae6b03699"
}
},
"source": [
"# Summary\n",
"In this notebook, I showed you how you can read a Git log output in only one line by using Pandas' read_csv method. This is a very handy method and a good starting point for further analyses! "
]
}
],
"metadata": {
"anaconda-cloud": {},
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.6.1"
},
"nbpresent": {
"slides": {
"1c3bf261-5fad-42f1-be29-bc970f66b78f": {
"id": "1c3bf261-5fad-42f1-be29-bc970f66b78f",
"prev": "2f379bd9-addb-4c73-aff7-0effc56da804",
"regions": {
"05f3625e-4608-4f56-b0c8-42757d38085e": {
"attrs": {
"height": 1,
"width": 1,
"x": 0,
"y": 0
},
"content": {
"cell": "cffa207d-719c-4ea3-ae5c-556ca5ee692e",
"part": "whole"
},
"id": "05f3625e-4608-4f56-b0c8-42757d38085e"
}
}
},
"2f379bd9-addb-4c73-aff7-0effc56da804": {
"id": "2f379bd9-addb-4c73-aff7-0effc56da804",
"prev": "fa62ca0d-9305-405d-a301-04fab08ee8f3",
"regions": {
"ac003123-264d-4b81-95cd-d79b47595f27": {
"attrs": {
"height": 1,
"width": 1,
"x": 0,
"y": 0
},
"content": {
"cell": "022f08fe-2746-4631-b0e2-1df34c154675",
"part": "whole"
},
"id": "ac003123-264d-4b81-95cd-d79b47595f27"
}
}
},
"3935cf8e-ea1d-4e1d-ad46-fcb10946cd1d": {
"id": "3935cf8e-ea1d-4e1d-ad46-fcb10946cd1d",
"prev": "88ab58e4-fc80-487b-83b5-687302c35522",
"regions": {
"a666344f-e0ba-42c7-9f85-12abfa643611": {
"attrs": {
"height": 1,
"width": 1,
"x": 0,
"y": 0
},
"content": {
"cell": "b38ec597-d6b6-4b10-ba27-709b9415b124",
"part": "whole"
},
"id": "a666344f-e0ba-42c7-9f85-12abfa643611"
}
}
},
"46431dec-27a6-40c6-af95-5fb2f230ebd4": {
"id": "46431dec-27a6-40c6-af95-5fb2f230ebd4",
"prev": "90fbdba6-3c4e-436f-b4a1-a4a469dde026",
"regions": {
"ee88eafd-7388-4463-9651-9e672e3cf093": {
"attrs": {
"height": 1,
"width": 1,
"x": 0,
"y": 0
},
"content": {
"cell": "577cb8ae-fdd7-4835-93c0-3d52c7e7fa6d",
"part": "whole"
},
"id": "ee88eafd-7388-4463-9651-9e672e3cf093"
}
}
},
"467b68fa-ba68-43f0-be64-972fa50485c2": {
"id": "467b68fa-ba68-43f0-be64-972fa50485c2",
"prev": "46431dec-27a6-40c6-af95-5fb2f230ebd4",
"regions": {
"06523258-1daa-4bbe-9b61-1af7bac105f8": {
"attrs": {
"height": 1,
"width": 1,
"x": 0,
"y": 0
},
"content": {
"cell": "0a9c1f53-c819-4fad-abf6-fa6ddcc6e53e",
"part": "whole"
},
"id": "06523258-1daa-4bbe-9b61-1af7bac105f8"
}
}
},
"5ce0a5c0-69e6-403c-8b9e-e91a4f6d2084": {
"id": "5ce0a5c0-69e6-403c-8b9e-e91a4f6d2084",
"prev": "467b68fa-ba68-43f0-be64-972fa50485c2",
"regions": {
"447d5ea1-72a7-484d-b127-7c87b2fb17a5": {
"attrs": {
"height": 1,
"width": 1,
"x": 0,
"y": 0
},
"content": {
"cell": "0a64edd8-1d4e-4456-8d54-db30f82da038",
"part": "whole"
},
"id": "447d5ea1-72a7-484d-b127-7c87b2fb17a5"
}
}
},
"69bc04e6-b390-4c39-8cfc-e901363073db": {
"id": "69bc04e6-b390-4c39-8cfc-e901363073db",
"prev": "b7d251b4-9300-4d87-a4c2-ce9e82cceaf1",
"regions": {
"d71fa94c-1908-4c10-8468-35ec4c0d0b60": {
"attrs": {
"height": 1,
"width": 1,
"x": 0,
"y": 0
},
"content": {
"cell": "f1852e51-8531-4ccc-af20-8348509cfc9c",
"part": "whole"
},
"id": "d71fa94c-1908-4c10-8468-35ec4c0d0b60"
}
}
},
"88ab58e4-fc80-487b-83b5-687302c35522": {
"id": "88ab58e4-fc80-487b-83b5-687302c35522",
"prev": "69bc04e6-b390-4c39-8cfc-e901363073db",
"regions": {
"08604d82-a07c-41b3-ad38-e27b39ee83a2": {
"attrs": {
"height": 1,
"width": 1,
"x": 0,
"y": 0
},
"content": {
"cell": "86a45314-4211-4cd1-93fb-9c156534a2e0",
"part": "whole"
},
"id": "08604d82-a07c-41b3-ad38-e27b39ee83a2"
}
}
},
"90fbdba6-3c4e-436f-b4a1-a4a469dde026": {
"id": "90fbdba6-3c4e-436f-b4a1-a4a469dde026",
"prev": "3935cf8e-ea1d-4e1d-ad46-fcb10946cd1d",
"regions": {
"66d3717e-256a-4063-96cf-415ced19032d": {
"attrs": {
"height": 1,
"width": 1,
"x": 0,
"y": 0
},
"content": {
"cell": "e34097f6-1626-49ba-bb93-b5b2dfb20d8c",
"part": "whole"
},
"id": "66d3717e-256a-4063-96cf-415ced19032d"
}
}
},
"b7d251b4-9300-4d87-a4c2-ce9e82cceaf1": {
"id": "b7d251b4-9300-4d87-a4c2-ce9e82cceaf1",
"prev": null,
"regions": {
"0e54fbca-ee14-4d41-8134-a7e4848c8244": {
"attrs": {
"height": 1,
"width": 1,
"x": 0,
"y": 0
},
"content": {
"cell": "4e3ff556-2ed1-4264-9e5d-c34080b5ef43",
"part": "whole"
},
"id": "0e54fbca-ee14-4d41-8134-a7e4848c8244"
}
}
},
"ce90176b-054d-44da-82e5-24e00f159d44": {
"id": "ce90176b-054d-44da-82e5-24e00f159d44",
"prev": "1c3bf261-5fad-42f1-be29-bc970f66b78f",
"regions": {
"a049b370-d20d-4b31-b12d-d1e98cf186a5": {
"attrs": {
"height": 1,
"width": 1,
"x": 0,
"y": 0
},
"content": {
"cell": "edfb29bb-0a19-4835-be91-60f8ae4941ca",
"part": "whole"
},
"id": "a049b370-d20d-4b31-b12d-d1e98cf186a5"
}
}
},
"fa62ca0d-9305-405d-a301-04fab08ee8f3": {
"id": "fa62ca0d-9305-405d-a301-04fab08ee8f3",
"prev": "5ce0a5c0-69e6-403c-8b9e-e91a4f6d2084",
"regions": {
"e33a8ad9-898a-435b-9cd8-a0a9e971b7c0": {
"attrs": {
"height": 1,
"width": 1,
"x": 0,
"y": 0
},
"content": {
"cell": "81cdfe40-3a5d-44cf-9b55-eaf0a635761f",
"part": "whole"
},
"id": "e33a8ad9-898a-435b-9cd8-a0a9e971b7c0"
}
}
}
},
"themes": {
"default": "66eaa5a2-7a73-471f-ab56-5b57ef30cb08",
"theme": {
"66eaa5a2-7a73-471f-ab56-5b57ef30cb08": {
"id": "66eaa5a2-7a73-471f-ab56-5b57ef30cb08",
"palette": {
"19cc588f-0593-49c9-9f4b-e4d7cc113b1c": {
"id": "19cc588f-0593-49c9-9f4b-e4d7cc113b1c",
"rgb": [
252,
252,
252
]
},
"31af15d2-7e15-44c5-ab5e-e04b16a89eff": {
"id": "31af15d2-7e15-44c5-ab5e-e04b16a89eff",
"rgb": [
68,
68,
68
]
},
"50f92c45-a630-455b-aec3-788680ec7410": {
"id": "50f92c45-a630-455b-aec3-788680ec7410",
"rgb": [
155,
177,
192
]
},
"c5cc3653-2ee1-402a-aba2-7caae1da4f6c": {
"id": "c5cc3653-2ee1-402a-aba2-7caae1da4f6c",
"rgb": [
43,
126,
184
]
},
"efa7f048-9acb-414c-8b04-a26811511a21": {
"id": "efa7f048-9acb-414c-8b04-a26811511a21",
"rgb": [
25.118061674008803,
73.60176211453744,
107.4819383259912
]
}
},
"rules": {
"blockquote": {
"color": "50f92c45-a630-455b-aec3-788680ec7410"
},
"code": {
"font-family": "Anonymous Pro"
},
"h1": {
"color": "c5cc3653-2ee1-402a-aba2-7caae1da4f6c",
"font-family": "Lato",
"font-size": 8
},
"h2": {
"color": "c5cc3653-2ee1-402a-aba2-7caae1da4f6c",
"font-family": "Lato",
"font-size": 6
},
"h3": {
"color": "50f92c45-a630-455b-aec3-788680ec7410",
"font-family": "Lato",
"font-size": 5.5
},
"h4": {
"color": "c5cc3653-2ee1-402a-aba2-7caae1da4f6c",
"font-family": "Lato",
"font-size": 5
},
"h5": {
"font-family": "Lato"
},
"h6": {
"font-family": "Lato"
},
"h7": {
"font-family": "Lato"
},
"pre": {
"font-family": "Anonymous Pro",
"font-size": 4
}
},
"text-base": {
"font-family": "Merriweather",
"font-size": 4
}
},
"ecdad483-e20b-4d01-87c5-efd37e40b00e": {
"backgrounds": {
"backgroundColor": {
"background-color": "backgroundColor",
"id": "backgroundColor"
}
},
"id": "ecdad483-e20b-4d01-87c5-efd37e40b00e",
"palette": {
"backgroundColor": {
"id": "backgroundColor",
"rgb": [
256,
256,
256
]
},
"headingColor": {
"id": "headingColor",
"rgb": [
34,
34,
34
]
},
"linkColor": {
"id": "linkColor",
"rgb": [
42,
118,
221
]
},
"mainColor": {
"id": "mainColor",
"rgb": [
34,
34,
34
]
}
},
"rules": {
"a": {
"color": "linkColor"
},
"h1": {
"color": "headingColor",
"font-family": "Source Sans Pro",
"font-size": 5.25
},
"h2": {
"color": "headingColor",
"font-family": "Source Sans Pro",
"font-size": 4
},
"h3": {
"color": "headingColor",
"font-family": "Source Sans Pro",
"font-size": 3.5
},
"h4": {
"color": "headingColor",
"font-family": "Source Sans Pro",
"font-size": 3
},
"h5": {
"color": "headingColor",
"font-family": "Source Sans Pro"
},
"h6": {
"color": "headingColor",
"font-family": "Source Sans Pro"
},
"h7": {
"color": "headingColor",
"font-family": "Source Sans Pro"
},
"li": {
"color": "mainColor",
"font-family": "Source Sans Pro",
"font-size": 6
},
"p": {
"color": "mainColor",
"font-family": "Source Sans Pro",
"font-size": 6
}
},
"text-base": {
"color": "mainColor",
"font-family": "Source Sans Pro",
"font-size": 6
}
}
}
}
}
},
"nbformat": 4,
"nbformat_minor": 1
}