Skip to content

Instantly share code, notes, and snippets.

@feststelltaste
Last active January 31, 2020 18:22
Show Gist options
  • Save feststelltaste/3bcbbebd5fc6e152ea82d40b2de2e41f to your computer and use it in GitHub Desktop.
Save feststelltaste/3bcbbebd5fc6e152ea82d40b2de2e41f to your computer and use it in GitHub Desktop.
Git Analysis Rename Problem (Draft)
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Some problems when analyzing Git logs\n",
"\n",
"In the past, I did a lot of Git log analysis on my blog. The main reason is that developers know what Git is and what kind of data it provides. So it is easy to connect to developers then doing more advanced analysis of Git data.\n",
"\n",
"But there is an area of problems with these kinds of analysis when you want to do file-based analysis in a long-running repository: Deletions, merges, splits and renames.\n",
"\n",
"For the latter one, I want to show you the kinds of problems in this notebook:"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Git Example repository\n",
"\n",
"For this analysis, we want to use a little but long-lived repository: The Spring PetClinic project (anti-refactored by me to show some interesting things).\n",
"\n",
"We first clone this repository locally."
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"Cloning into 'spring-petclinic'...\n",
"Checking out files: 100% (549/549), done.\n"
]
}
],
"source": [
"%%bash\n",
"\n",
"git clone https://github.com/JavaOnAutobahn/spring-petclinic"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Next, we export the Git history by using a special command (background explained [here](https://www.feststelltaste.de/reading-a-git-repos-commit-history-with-pandas-efficiently/))"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"%%bash\n",
"\n",
"# path to git repository\n",
"cd spring-petclinic\n",
"git log --numstat --pretty=format:\"%x09%x09%x09%ai\" -- *.java > git_log.csv"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"With a little helper function, we import the exported data (see link above for details on that as well)."
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>additions</th>\n",
" <th>deletions</th>\n",
" <th>churn</th>\n",
" <th>filename</th>\n",
" </tr>\n",
" <tr>\n",
" <th>timestamp</th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <td>2019-03-05 22:32:20+01:00</td>\n",
" <td>1.0</td>\n",
" <td>1.0</td>\n",
" <td>0.0</td>\n",
" <td>src/main/java/org/springframework/samples/petc...</td>\n",
" </tr>\n",
" <tr>\n",
" <td>2019-03-05 22:32:20+01:00</td>\n",
" <td>2.0</td>\n",
" <td>0.0</td>\n",
" <td>2.0</td>\n",
" <td>src/main/java/org/springframework/samples/petc...</td>\n",
" </tr>\n",
" <tr>\n",
" <td>2019-03-05 22:32:20+01:00</td>\n",
" <td>2.0</td>\n",
" <td>1.0</td>\n",
" <td>1.0</td>\n",
" <td>src/main/java/org/springframework/samples/petc...</td>\n",
" </tr>\n",
" <tr>\n",
" <td>2019-03-05 22:32:20+01:00</td>\n",
" <td>2.0</td>\n",
" <td>0.0</td>\n",
" <td>2.0</td>\n",
" <td>src/main/java/org/springframework/samples/petc...</td>\n",
" </tr>\n",
" <tr>\n",
" <td>2019-03-05 22:32:20+01:00</td>\n",
" <td>3.0</td>\n",
" <td>0.0</td>\n",
" <td>3.0</td>\n",
" <td>src/main/java/org/springframework/samples/petc...</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" additions deletions churn \\\n",
"timestamp \n",
"2019-03-05 22:32:20+01:00 1.0 1.0 0.0 \n",
"2019-03-05 22:32:20+01:00 2.0 0.0 2.0 \n",
"2019-03-05 22:32:20+01:00 2.0 1.0 1.0 \n",
"2019-03-05 22:32:20+01:00 2.0 0.0 2.0 \n",
"2019-03-05 22:32:20+01:00 3.0 0.0 3.0 \n",
"\n",
" filename \n",
"timestamp \n",
"2019-03-05 22:32:20+01:00 src/main/java/org/springframework/samples/petc... \n",
"2019-03-05 22:32:20+01:00 src/main/java/org/springframework/samples/petc... \n",
"2019-03-05 22:32:20+01:00 src/main/java/org/springframework/samples/petc... \n",
"2019-03-05 22:32:20+01:00 src/main/java/org/springframework/samples/petc... \n",
"2019-03-05 22:32:20+01:00 src/main/java/org/springframework/samples/petc... "
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"import pandas as pd\n",
"\n",
"def parse_git_log(path):\n",
" # reading\n",
" git_log = pd.read_csv(\n",
" path,\n",
" sep=\"\\t\", \n",
" header=None,\n",
" names=[\n",
" 'additions', \n",
" 'deletions', \n",
" 'filename', \n",
" 'timestamp'])\n",
"\n",
" # converting in \"one line\"\n",
" git_log = git_log[['additions', 'deletions', 'filename']]\\\n",
" .join(git_log[['timestamp']]\\\n",
" .fillna(method='ffill'))\\\n",
" .dropna().reset_index(drop=True)\n",
"\n",
" # data type conversions\n",
" git_log['additions'] = pd.to_numeric(git_log['additions'], errors='coerce')\n",
" git_log['deletions'] = pd.to_numeric(git_log['deletions'], errors='coerce')\n",
" churn = git_log['additions'] - git_log['deletions']\n",
" git_log.insert(2, \"churn\", churn)\n",
" git_log['timestamp'] = pd.to_datetime(git_log['timestamp'])\n",
" return git_log.set_index('timestamp')\n",
"\n",
"timed_log = parse_git_log(\"spring-petclinic/git_log.csv\")\n",
"timed_log.head()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"So what we got is a nice parsed pandas dataframe we can use for further analysis.\n",
"\n",
"## Analysis\n",
"Let's dive into the actual problem analysis. Say we want to do some file-based analysis of the software project with data based on Git. So we group our features along the timestamps.\n",
"\n",
"(Note that we keep the last timestamp entry for each file to do an analysis based on the most recent data later on)."
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>additions</th>\n",
" <th>deletions</th>\n",
" <th>churn</th>\n",
" <th>timestamp</th>\n",
" </tr>\n",
" <tr>\n",
" <th>filename</th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <td>org.springframework.samples.petclinic/src/main/java/org/springframework/samples/petclinic/HomeController.java</td>\n",
" <td>17.0</td>\n",
" <td>17.0</td>\n",
" <td>0.0</td>\n",
" <td>2013-01-09 17:24:48+08:00</td>\n",
" </tr>\n",
" <tr>\n",
" <td>org.springframework.samples.petclinic/src/main/java/org/springframework/samples/petclinic/appointments/Appointment.java</td>\n",
" <td>37.0</td>\n",
" <td>37.0</td>\n",
" <td>0.0</td>\n",
" <td>2013-01-09 17:24:48+08:00</td>\n",
" </tr>\n",
" <tr>\n",
" <td>org.springframework.samples.petclinic/src/main/java/org/springframework/samples/petclinic/appointments/AppointmentBook.java</td>\n",
" <td>13.0</td>\n",
" <td>13.0</td>\n",
" <td>0.0</td>\n",
" <td>2013-01-09 17:24:48+08:00</td>\n",
" </tr>\n",
" <tr>\n",
" <td>org.springframework.samples.petclinic/src/main/java/org/springframework/samples/petclinic/appointments/AppointmentForm.java</td>\n",
" <td>67.0</td>\n",
" <td>67.0</td>\n",
" <td>0.0</td>\n",
" <td>2013-01-09 17:24:48+08:00</td>\n",
" </tr>\n",
" <tr>\n",
" <td>org.springframework.samples.petclinic/src/main/java/org/springframework/samples/petclinic/appointments/Appointments.java</td>\n",
" <td>15.0</td>\n",
" <td>15.0</td>\n",
" <td>0.0</td>\n",
" <td>2013-01-09 17:24:48+08:00</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" additions deletions \\\n",
"filename \n",
"org.springframework.samples.petclinic/src/main/... 17.0 17.0 \n",
"org.springframework.samples.petclinic/src/main/... 37.0 37.0 \n",
"org.springframework.samples.petclinic/src/main/... 13.0 13.0 \n",
"org.springframework.samples.petclinic/src/main/... 67.0 67.0 \n",
"org.springframework.samples.petclinic/src/main/... 15.0 15.0 \n",
"\n",
" churn \\\n",
"filename \n",
"org.springframework.samples.petclinic/src/main/... 0.0 \n",
"org.springframework.samples.petclinic/src/main/... 0.0 \n",
"org.springframework.samples.petclinic/src/main/... 0.0 \n",
"org.springframework.samples.petclinic/src/main/... 0.0 \n",
"org.springframework.samples.petclinic/src/main/... 0.0 \n",
"\n",
" timestamp \n",
"filename \n",
"org.springframework.samples.petclinic/src/main/... 2013-01-09 17:24:48+08:00 \n",
"org.springframework.samples.petclinic/src/main/... 2013-01-09 17:24:48+08:00 \n",
"org.springframework.samples.petclinic/src/main/... 2013-01-09 17:24:48+08:00 \n",
"org.springframework.samples.petclinic/src/main/... 2013-01-09 17:24:48+08:00 \n",
"org.springframework.samples.petclinic/src/main/... 2013-01-09 17:24:48+08:00 "
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"file_churns = timed_log.reset_index().groupby('filename').agg({\n",
" \"additions\" : \"sum\",\n",
" \"deletions\" : \"sum\",\n",
" \"churn\" : \"sum\",\n",
" \"timestamp\" : \"first\"\n",
"})\n",
"file_churns.head()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"So, at this point, something weird happens: **There are files that have a negative number of lines!**\n",
"\n",
"How can this happen?"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>additions</th>\n",
" <th>deletions</th>\n",
" <th>churn</th>\n",
" <th>timestamp</th>\n",
" </tr>\n",
" <tr>\n",
" <th>filename</th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <td>src/test/java/org/springframework/samples/petclinic/service/ClinicServiceJpaTests.java</td>\n",
" <td>11.0</td>\n",
" <td>13.0</td>\n",
" <td>-2.0</td>\n",
" <td>2018-11-15 18:39:27+01:00</td>\n",
" </tr>\n",
" <tr>\n",
" <td>src/main/java/org/springframework/samples/petclinic/repository/jpa/package-info.java</td>\n",
" <td>0.0</td>\n",
" <td>3.0</td>\n",
" <td>-3.0</td>\n",
" <td>2015-10-16 09:33:06+02:00</td>\n",
" </tr>\n",
" <tr>\n",
" <td>src/main/java/org/springframework/samples/petclinic/repository/jdbc/package-info.java</td>\n",
" <td>0.0</td>\n",
" <td>3.0</td>\n",
" <td>-3.0</td>\n",
" <td>2015-10-16 09:33:06+02:00</td>\n",
" </tr>\n",
" <tr>\n",
" <td>src/main/java/org/springframework/samples/petclinic/web/VetsAtomView.java</td>\n",
" <td>56.0</td>\n",
" <td>130.0</td>\n",
" <td>-74.0</td>\n",
" <td>2015-05-12 19:07:35+08:00</td>\n",
" </tr>\n",
" <tr>\n",
" <td>src/test/java/org/springframework/samples/petclinic/web/VisitsViewTests.java</td>\n",
" <td>7.0</td>\n",
" <td>77.0</td>\n",
" <td>-70.0</td>\n",
" <td>2015-05-10 06:45:39+08:00</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" additions deletions \\\n",
"filename \n",
"src/test/java/org/springframework/samples/petcl... 11.0 13.0 \n",
"src/main/java/org/springframework/samples/petcl... 0.0 3.0 \n",
"src/main/java/org/springframework/samples/petcl... 0.0 3.0 \n",
"src/main/java/org/springframework/samples/petcl... 56.0 130.0 \n",
"src/test/java/org/springframework/samples/petcl... 7.0 77.0 \n",
"\n",
" churn \\\n",
"filename \n",
"src/test/java/org/springframework/samples/petcl... -2.0 \n",
"src/main/java/org/springframework/samples/petcl... -3.0 \n",
"src/main/java/org/springframework/samples/petcl... -3.0 \n",
"src/main/java/org/springframework/samples/petcl... -74.0 \n",
"src/test/java/org/springframework/samples/petcl... -70.0 \n",
"\n",
" timestamp \n",
"filename \n",
"src/test/java/org/springframework/samples/petcl... 2018-11-15 18:39:27+01:00 \n",
"src/main/java/org/springframework/samples/petcl... 2015-10-16 09:33:06+02:00 \n",
"src/main/java/org/springframework/samples/petcl... 2015-10-16 09:33:06+02:00 \n",
"src/main/java/org/springframework/samples/petcl... 2015-05-12 19:07:35+08:00 \n",
"src/test/java/org/springframework/samples/petcl... 2015-05-10 06:45:39+08:00 "
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"weird_churns = file_churns[file_churns['churn'] < 0].sort_values(by=\"timestamp\", ascending=False)\n",
"weird_churns.head()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Let's look at a more recent file with such a negative number of lines (\"recent\" because then it is more likely that it still exists in the repository)."
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'src/test/java/org/springframework/samples/petclinic/service/ClinicServiceJpaTests.java'"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"weird_churn_filename = weird_churns.iloc[0].name\n",
"weird_churn_filename"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"For this file, we want to follow the development. Using the `--follow` option if Git, we can trace the evolution of this single file. As in the first Git data export, we store this data into a file."
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [],
"source": [
"%%bash\n",
"cd spring-petclinic\n",
"git log --numstat --pretty=format:\"%x09%x09%x09%ai\" --follow src/test/java/org/springframework/samples/petclinic/service/ClinicServiceJpaTests.java > ../weird_churn_filename_log.csv"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Let's read in the data with our little helper function from above."
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>additions</th>\n",
" <th>deletions</th>\n",
" <th>churn</th>\n",
" <th>filename</th>\n",
" </tr>\n",
" <tr>\n",
" <th>timestamp</th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <td>2018-11-15 18:39:27+01:00</td>\n",
" <td>3.0</td>\n",
" <td>2.0</td>\n",
" <td>1.0</td>\n",
" <td>src/test/java/org/springframework/samples/petc...</td>\n",
" </tr>\n",
" <tr>\n",
" <td>2015-10-16 09:33:06+02:00</td>\n",
" <td>3.0</td>\n",
" <td>4.0</td>\n",
" <td>-1.0</td>\n",
" <td>src/test/java/org/springframework/samples/petc...</td>\n",
" </tr>\n",
" <tr>\n",
" <td>2013-12-16 20:58:15+09:00</td>\n",
" <td>2.0</td>\n",
" <td>1.0</td>\n",
" <td>1.0</td>\n",
" <td>src/test/java/org/springframework/samples/petc...</td>\n",
" </tr>\n",
" <tr>\n",
" <td>2013-06-28 12:00:29+08:00</td>\n",
" <td>1.0</td>\n",
" <td>2.0</td>\n",
" <td>-1.0</td>\n",
" <td>src/test/java/org/springframework/samples/petc...</td>\n",
" </tr>\n",
" <tr>\n",
" <td>2013-03-04 12:15:20+08:00</td>\n",
" <td>2.0</td>\n",
" <td>4.0</td>\n",
" <td>-2.0</td>\n",
" <td>src/test/java/org/springframework/samples/petc...</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" additions deletions churn \\\n",
"timestamp \n",
"2018-11-15 18:39:27+01:00 3.0 2.0 1.0 \n",
"2015-10-16 09:33:06+02:00 3.0 4.0 -1.0 \n",
"2013-12-16 20:58:15+09:00 2.0 1.0 1.0 \n",
"2013-06-28 12:00:29+08:00 1.0 2.0 -1.0 \n",
"2013-03-04 12:15:20+08:00 2.0 4.0 -2.0 \n",
"\n",
" filename \n",
"timestamp \n",
"2018-11-15 18:39:27+01:00 src/test/java/org/springframework/samples/petc... \n",
"2015-10-16 09:33:06+02:00 src/test/java/org/springframework/samples/petc... \n",
"2013-12-16 20:58:15+09:00 src/test/java/org/springframework/samples/petc... \n",
"2013-06-28 12:00:29+08:00 src/test/java/org/springframework/samples/petc... \n",
"2013-03-04 12:15:20+08:00 src/test/java/org/springframework/samples/petc... "
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"weird_file_churn = parse_git_log(\"weird_churn_filename_log.csv\")\n",
"weird_file_churn.head()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Insights\n",
"\n",
"OK, what is the problem with the negative number of lines?\n",
"\n",
"Let's look at the history of this one specific file: It was **renamed** several times!"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"src/test/java/org/springframework/samples/petclinic/service/ClinicServiceJpaTests.java 5\n",
"src/test/java/org/springframework/samples/petclinic/jpa/AbstractJpaClinicTests.java 4\n",
"src/test/java/org/springframework/samples/petclinic/jpa/JpaClinicTests.java 3\n",
"src/test/java/org/springframework/samples/petclinic/jpa/JpaOwnerRepositoryImplTests.java 2\n",
"src/test/java/org/springframework/samples/petclinic/repository/jpa/JpaOwnerRepositoryImplTests.java 2\n",
"src/test/java/org/springframework/samples/petclinic/jpa/{JpaClinicTests.java => JpaClinicImplTests.java} 1\n",
"src/test/java/org/springframework/samples/petclinic/jpa/{AbstractJpaClinicTests.java => JpaClinicTests.java} 1\n",
"src/test/java/org/springframework/samples/petclinic/jpa/{JpaClinicImplTests.java => JpaOwnerRepositoryImplTests.java} 1\n",
"src/test/java/org/springframework/samples/petclinic/{ => repository}/jpa/JpaOwnerRepositoryImplTests.java 1\n",
"src/test/java/org/springframework/samples/petclinic/{repository/jpa/JpaOwnerRepositoryImplTests.java => service/ClinicServiceJpaTests.java} 1\n",
"Name: filename, dtype: int64"
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"weird_file_churn['filename'].value_counts()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Albeit Git provides rename tracking features, some of the renames aren't renames compliant to the Git rename approach (the ones with the `=>` are the ones that Git can track) and thus making it difficult to track those renames with standard means.\n",
"\n",
"If we now sum up all the `churn` values for these files, we get the actual number of lines for the files based on pure Git repository data."
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {
"scrolled": true
},
"outputs": [
{
"data": {
"text/plain": [
"23.0"
]
},
"execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"weird_file_churn['churn'].sum()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Let's compare this one with the actual number of lines in the real file using the word count comment `wc`."
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"23 spring-petclinic/src/test/java/org/springframework/samples/petclinic/service/ClinicServiceJpaTests.java\n"
]
}
],
"source": [
"%%bash\n",
"wc -l spring-petclinic/src/test/java/org/springframework/samples/petclinic/service/ClinicServiceJpaTests.java"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Cool, this one matches! This might not be always the case for example if you do some weird renaming actions with your source code base or to some merges or splitting ups of files."
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"-2.0"
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"weird_file_churn[weird_file_churn['filename'] == weird_churn_filename]['churn'].sum()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Visualization\n",
"\n",
"Let's look at the number of lines for this specific file to get a feeling if the data is right at all.\n"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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oIO6582aNSuzZs3qtd7ixYtz7rnnZrfddktzc3OmTp2a17/+9fn+97/fa+yqe5188smZP39+jjvuuMyYMSMTJ07MPvvskx/96Ee95ixZsiTf+MY3stdee6WlpSXNzc2ZNWtWjjzyyNx4443r/FyG04jYOq2UsmOSZ6qqemqN8w1JPpPkpVkRHFq48tLVSb6Y5LhSygVVVd21cvzEJJ9dOeZf1rjNpUk+keT0UsqlVVXNXzmnJck/rRzzzcF4HkEjAAAAAAAAAGAs+cAHPpCdd945BxxwQLbccsssWLAgP/7xj3PiiSfmoYceymc+85nusbfffnve+MY3ZsmSJTnqqKPyile8Ivfdd18OOuigHHLIIX2u/7WvfS0f/ehHM23atJx00kmZNm1abrjhhuy3337ZfPPN11vftGnTcs4552Tu3Ll59NFHc84553Rf6ytYVGvJkiU59NBDc8stt2THHXfMaaedls7Ozlx99dU59thjc9999+Xzn/98r3mPPvpoZs+ene222y4nnnhi2tvbc9VVV3WHhw4++ODusSeffHKuvPLK7LrrrjnppJMyadKk/OlPf8q8efNy/fXX541vfON6n3E4jIigUVZ0GTqvlHJrkt8nWZDkZUkOTLJdkqeSvG/V4KqqniulvC8rAkdtpZTvJWlPckSSHVaev6r2BlVV/aGUclaSbyS5q5RyVZIlSY5JslWSr1RVte5oXD8JGgEAAAAAAAAAY8mvf/3rXluOLVmyJIcffnjOPffcnHrqqZk5c2aqqsopp5ySF154Iddcc02OPPLI7vHnn39+zjzzzF5rz58/P//wD/+QlpaW3HPPPd3BoC984Qt5xzvekR/84AfrrW/atGmZM2dO2tra8uijj3Z3OuqPr3zlK7nlllty+OGH54c//GGamlbEbc4555zMnj07X/jCF/LWt741++23X495bW1tmTNnTo9Q0/HHH5/DDjss5513XnfQ6Nlnn+3u1nTHHXeksbGxxzoLFizod61DbaQEjW5M8v8l2T/J7kmmJelI8tsk30nyjaqq2msnVFV1TSnlwCSfTHJ0kolJfpfkoyvHV2vepKqqC0op85N8PMlJWbF13ANJPlVV1WWD9TALOwWNAAAAAAAAAGCsm/UP/13vEvpt/rlv2aj5a4aMkmT8+PE57bTT8rOf/Sw33XRTTjrppNx+++156KGHcsABB/QIGSXJ6aefngsuuCC///3ve5y/4oorsmTJkvzDP/xDj+5DDQ0NOe+883LNNdekq6tro+pfl0suuSSllHz1q1/tDhklyUtf+tKcffbZee9735tvf/vbvYJG22yzTT71qU/1OHfooYfm5S9/ee68887uc6WUVFWVCRMmpKGhodf9p0+fPshPNHAjImhUVdWvk5w2gHn/k+TNGzjnuiTXbei9NsSC5wWNAAAAAAAAAICx449//GO++MUv5qabbsof//jHvPDCCz2uP/HEE0mSe+65J0ly4IEH9lqjsbExr3vd63oFjdY1Z7vttsvWW2+dRx99dFCeY02LFi3K7373u8ycOTM77rhjr+urtnq79957e13bY489enUnSpKtt946P//56k23pk6dmre97W257rrrsscee+Too4/O61//+uy7775pbm4exKfZeCMiaLSp0dEIAAAAAAAAABgrHnnkkcyePTsLFy7M61//+rzpTW/K5ptvnsbGxsyfPz+XXXZZFi9enGTFNmFJ8rKXvazPtbbYYote5/ozZ6iCRqvuveWWW/Z5fdX5Z555pte1adOm9TmnqampVwemq666Kl/84hfz3e9+t3urtYkTJ+aYY47Jl7/85bU++3ATNBoCCzoEjQAAAAAAAABgrNvY7chGi69+9atZsGBBLr300px88sk9rl155ZW57LLLut9vvvnmSZI///nPfa711FNP9TpXO2eXXXbp15zBsurea7vHk08+2WPcQE2aNClz5szJnDlz8thjj+XWW2/N3Llzc/nll2f+/Pm57bbbNmr9wdJ7Yzc22kJBIwAAAAAAAABgjPjd736XJDn66KN7Xbvlllt6vN9rr736PJ8ky5cvz7x583qdX9ecRx55JI899li/a121ldny5cv7NX7KlCnZfvvt88QTT+Thhx/udf3mm2/uUeNg2HrrrXPCCSfkhhtuyCtf+crMmzcvCxYsGLT1N4ag0RBoFzQCAAAAAAAAAMaIWbNmJUna2tp6nL/hhhvy7W9/u8e5/fbbLzvssENuvfXWXHvttT2uXXjhhfn973/fa/0TTjgh48aNywUXXJD58+d3n+/q6spZZ53VaxuydZk+fXqS5I9//GO/55xyyimpqipnnXVWj4DSX//613zmM5/pHjNQTz/9dO64445e5zs6OrJo0aI0NTVl/PjxA15/MNk6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7pkLL7wwXV1d3ePmzJmTbbfdNkly2WWXpZTS/W/VNmptbW0ppWTOnDm97vPwww/npJNOysyZMzN+/Pj8zd/8TU466aQ8/PDDvcbOmTMnpZS0tbXl6quvzuzZs9Pc3JzW1tYcd9xxeeKJJ3rNeeSRR/L+978/r3jFKzJp0qS0trbm1a9+dU499dQsWLCg35/HUNPRaJDVdjRaqKMRAACMXT2CRjoaAQAAAACbtjPPPDPf+MY3suWWW+b9739/xo0bl2uvvTZ33HFHlixZkvHjx/cY/573vCeXXHJJttpqqxx11FGZNm1afvGLX+Tss8/OTTfdlJ/+9Kdpalp3rGXp0qV529velhtuuCE77LBDjj/++EycODE333xzPvShD+WOO+7Id77znSTJQQcdlGeeeSbnn39+dt999/zv//2/u9fZY4891nmfX/7yl3njG9+YRYsW5YgjjsjOO++c3/zmN7niiity7bXX5qabbso+++zTa97FF1+cH/7whzniiCNy4IEH5o477shVV12V+++/P/fdd18mTJiQJHnyySfzv/7X/8pzzz2XN7/5zTn66KPz4osv5g9/+EO+853v5PTTT8/06dP79XMYaoJGg6ypoSHLVx4v6BA0AgCAMWtyzR7eOhoBAAAAAJuw22+/Pd/4xjey/fbb584770xra2uS5HOf+1wOPvjgPPnkk9lmm226x8+dOzeXXHJJ3v72t+eKK67IpEmTuq/NmTMnn/70p3PRRRflwx/+8Drv+7nPfS433HBDTj/99Hz9619PY2NjkmT58uV5//vfn0suuSTHHHNMjjzyyBx00EGZNWtWzj///Oyxxx59di3qS1VVOemkk/Lcc8/l8ssvzwknnNB97aqrrspxxx2Xd73rXXnggQfS0NBzY7Hrr78+v/zlL/PqV7+6+9zxxx+fK6+8Mtdee23+7u/+Lkly9dVXp729PV//+td7PXNHR0evdetJ0GiQNTaU7qDRQkEjAAAYu5prg0Y6GgEAAADAmDRn83pX0H9z1r7F2fpceumlSZJPfvKT3SGjJJk4cWK+8IUv5OCDD+4x/vzzz09TU1MuueSSHiGjJDn77LNz4YUX5oorrlhn0KirqysXXnhhtthii3zta1/rDhklSWNjY77yla/k0ksvzRVXXJEjjzxywM92++235ze/+U1e+9rX9ggZJcmxxx6bCy+8MPPmzcu8efNywAEH9Lh+xhln9AgZJcn73ve+XHnllbnzzju7g0arrPlZJMnkyZMHXPtQEDQaZE2NJaviRe2CRgAAMHb12DpNRyMAAAAAYNN1zz33JEkOPPDAXtde//rX99gCrbOzM/fff39mzJiRr3/9632uN2HChDz44IPrvOdvf/vbLFiwIK985Svz2c9+ts8xkyZNWu8667Pq2Q455JA+rx9yyCGZN29e7r333l5Bo762U9t6662TJAsXLuw+d8QRR+Sf/umfctppp+WGG27IoYcemv333z8777xzSikbVf9gEzQaZE0Nq3/AgkYAADCG1QaNOnU0AgAAAAA2Xc8+u6Ib0ste9rJe1xobGzN9+vTu9wsXLkxVVXn66afz6U9/esD3XLBgQZLk4YcfXuc6zz///IDvkax+ti233LLP66vOP/PMM72uTZs2rde5VaGr5cuXd5/bZpttcuedd2bOnDm5/vrr84Mf/CDJilDSxz/+8Zxxxhkb9QyDSdBokDXWBo06l6SqqhGXLgMAAIbBZFunAQAAAMCYtxHbkY0mm2++You4P//5z9luu+16XFu+fHkWLFiQmTNn9hi75557dncL2ph7vv3tb+8O5gyFVfd56qmn+rz+5JNP9hg3UDvttFOuuuqqLFu2LPfff39uvPHGXHDBBfnwhz+cyZMn5z3vec9GrT9YGupdwKamoZRMaFrxsS5Z1pWOJcvXMwMAANgk9QgaPZ1UVf1qAQAAAAAYQnvttVeS5JZbbul17bbbbsuyZcu632+22WbZZZdd8v/+3/9Le3v7gO+54447Ztq0afnFL36RpUuX9mtOY2Njkp7dhNZnzz33TJK0tbX1eX3V+VWfwcZqamrK3nvvnb//+7/PlVdemSS55pprBmXtwSBoNASmTx7ffbzQ9mkAADA2jZ+cjGtecbx8SbL4ufrWAwAAAAAwRE4++eQkyec+97ke4aEXX3wx//iP/9hr/Ec/+tEsWbIkp5xySp9bji1cuHC93Y6ampryoQ99KE8++WTOOOOMvPDCC73GPPnkk3nggQe637e0tKSUkj/+8Y/9fbTsv//+2WGHHTJv3rxcffXVPa5dffXVufXWW/OqV70qr3vd6/q95pruvPPO/PnPf+51ftW55ubmAa892GydNgRaJo/Pn559MUnS3rEkW7eOnB84AAAwjCbPSJ5Z+Qtrx1+TiRvXOhcAAAAAYCTaf//986EPfSgXXHBBdt111xxzzDEZN25crr322rS0tGTLLbfsMf6UU07J3XffnYsvvjjbb799Dj300Lz85S9Pe3t7/vCHP+TWW2/N//k//yff/OY313nfs88+O/fff3+++c1v5rrrrsshhxySmTNn5i9/+Usefvjh/M///E8+97nPZeedd06yopvSvvvum9tuuy0nnHBCXvWqV6WxsTFHHHFEdttttz7vUUrJZZddlr/927/NsccemyOPPDI77rhjHnrooVxzzTWZMmVK/v3f/z0NDQPv9fPd7343F110UQ488MC84hWvSEtLS37/+9/nuuuuy4QJE3LmmWcOeO3BJmg0BFprOhq162gEAABj1+SX9AwaTd++vvUAAAAAAAyR888/P6961aty0UUX5V//9V8zffr0vP3tb8/nP//57L777r3GX3TRRTn88MPzzW9+MzfeeGOeeeaZtLa25uUvf3nOOuusvOtd71rvPceNG5drrrkml19+eebOnZsf/ehHef755/OSl7wk2267bT7zmc/khBNO6DHnO9/5Tj7ykY/k+uuvz5VXXpmqqrLVVlutNWiUJPvuu29++ctf5rOf/WxuvPHGXHfddZkxY0be+c535uyzz84OO+yw4R9YjXe+851ZvHhxbr/99txzzz154YUXMnPmzBx33HH52Mc+ll133XWj1h9MpaqqetewySil3L3XXnvt9bqzvp1r7/tTkuQr79g9R++9VZ0rAwAA6uK7xya/vX7F8bFXJDu9tb71AAAAAACD6sEHH0yS7LTTTnWuBPr/fdx7771zzz333FNV1d4beo+B921irVqaV3c0WtipoxEAAIxZk2esPu54un51AAAAAADAIBA0GgLTa7ZOW2DrNAAAGLuaa4NGf61fHQAAAAAAMAgEjYZAS03QaKGgEQAAjF2TX7L6uFPQCAAAAACA0U3QaAjoaAQAACTpGTSydRoAAAAAAKOcoNEQ0NEIAABIkkyu3TpN0AgAAAAAgNFN0GgItNYEjdo7BY0AAGDM6tHRyNZpAAAAAACMboJGQ6BH0EhHIwAAGLt0NAIAAAAAYBMiaDQEpk0a13387AtLs2x5Vx2rAQAA6qa5JmjUuSDp8rsBAAAAAACjl6DREGhqbMi05hVho6pKnnlhaZ0rAgAA6qJpfDJx8xXHVVfywsL61gMAAAAAABtB0GiItDav3j5toe3TAABg7Jr8ktXHtk8DAAAAAGAUEzQaIi2TVweN2gWNAABg7BI0AgAAAABgEyFoNERaBY0AAIAkaZ6++ljQCAAAAACAUUzQaIjUbp3W3iloBAAAY1ZtR6POBfWrAwAAAAAANpKg0RBp3awmaPS8oBEAAIxZtk4DAAAAAEiSzJ8/P6WUnHzyyfUuhQESNBoiOhoBAABJBI0AAAAAANhkCBoNkZbJq4NGCzsEjQAAYMyaPH31saARAAAAAACjmKDREJleEzRaIGgEAABjV4+ORgvqVwcAAAAAAGwkQazD8oMAACAASURBVKMh0qOjka3TAABg7LJ1GgAAAAAwRtx555059thjM3PmzEyYMCFbbrll3vSmN+X73/9+r7Hz58/PcccdlxkzZmTixInZZ5998qMf/ajXuDlz5qSUkra2tj7XKKXk5JNP7nH+5JNPTikljzzySC644ILstttumTRpUg466KAkSVtbW0opmTNnTu6777685S1vybRp09Lc3JwDDzwwt99++2B8HJskQaMhUtvRqP15QSMAABizBI0AAAAAgDHgW9/6Vvbbb79cc8012W+//fKxj30sb3nLW/KXv/wlF198cY+xjz76aGbPnp358+fnxBNPzLHHHptf//rXOfLII3PzzTcPWk0f/vCHc/bZZ+fVr351PvzhD2f//ffvcf2uu+7KfvvtlxdffDHvfe9789a3vjXz5s3LG97whjz00EODVsempKneBWyqajsatetoBAAAY9eklqQ0JFVX8uIzybIlSdP49c8DAAAAABglHnjggXzwgx/M1KlTc9ttt2WXXXbpcf3xxx/v8b6trS1z5szJOeec033u+OOPz2GHHZbzzjsvBx988KDUdc899+Tee+/Ntttu2+f1//7v/86ll17aoyPSv/7rv+bUU0/N+eef3ysghaDRkJk8vjHjGxuyZHlXXlzalReWLM+k8Y31LgsAABhuDY3JpNak868r3ncuSKZuWd+aAAAAAIBh8erLXl3vEvrt/777/w547r/8y79k2bJlOfvss3uFjJJkq6226vF+m222yac+9ake5w499NC8/OUvz5133jngOtb0iU98Yq0hoyTZf//9e227dsopp+T0008f1Do2JbZOGyKllLTWdDVa0LG4jtUAAAB1Vbt92qrAEQAAAADAJuIXv/hFkuTwww/v1/g99tgjjY29m7VsvfXWWbhw4aDVNXv27HVe32effXqdGzduXF72spcNah2bEkGjIVS7fdrCjqV1rAQAAKiryTNWH3c8Xb86AAAAAACGwDPPPJMkmTlzZr/GT5s2rc/zTU1N6erqGrS6tthiiwHXsXz58kGrY1Ni67Qh1Dp5XPexjkYAADCG1XY06tDRCAAAAADGio3Zjmw0WRXYeeKJJ7LjjjsO6toNDSt66CxbtqzXtVUBp7UppQxqLehoNKRaJ0/oPl7YuaSOlQAAAHU1cfPVxy8+W786AAAAAACGwGte85okyU9+8pNBX7ulpSVJ8thjj/W6dtdddw36/Vg3QaMh1Nq8uqNRu63TAABg7Cp+9QIAAAAANl0f+MAH0tTUlM985jN54IEHel1//PHHB7z27NmzkySXXnppj65Gjz32WP75n/95wOsyMLZOG0K1HY3abZ0GAAAAAAAAAGyCdt5551x88cU59dRTs+eee+bII4/MK1/5yixYsCB33XVXpkyZkptvvnlAa++777454IADcuutt2b27Nk55JBD8uc//znXXXddDj300D47HTF0/FntEGqdrKMRAAAAAAAAALDpe9/73pd58+blrW99a9ra2nLeeeflhz/8YWbMmJHTTjtto9a+9tpr8973vjePP/54Lrjggtx777350pe+lC9+8YuDVD39paPREGqZPL77WEcjAAAAAAAAAGBT9trXvjb/+Z//udbrs2bNSlVVa73e1tbW5/lp06blW9/6Vr71rW/1utbXenPnzs3cuXPXep+DDjponXXMnz9/rdfGOh2NhlBrTdBooY5GAAAAAAAAAACMYoJGQ6g2aNTeuaSOlQAAAHVVSr0rAAAAAACAjSZoNIR6BI06BI0AAAAAAAAAABi9BI2GUEvz6qDRM51Lsrxr7fv7AQAAAAAAAADASCZoNITGNTZkysSmJElXlTz7wtI6VwQAAAAAAAAAAAMjaDTEpts+DQAAqFXpdAoAAAAAwOgkaDTEWmqCRgs7BY0AAGBsKvUuAAAAAACATVg1TH/kKmg0xGo7Gi14XtAIAAAAAAAAADYVpaz4I8Ourq46V8JYtypotOo7OVQEjYZYS7OORgAAAAAAAACwKZowYUKSpKOjo86VMNat+g6u+k4OFUGjIdZa09GovUPQCAAAAAAAAAA2FVOmTEmS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\n",
"text/plain": [
"<Figure size 1440x360 with 1 Axes>"
]
},
"metadata": {
"image/png": {
"height": 300,
"width": 1165
},
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"%matplotlib inline\n",
"weird_file_churn[['additions', 'deletions', 'churn']].cumsum().plot(figsize=[20,5]);"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We see that somehow we got a negative number of lines of code at the beginning, which could be an indication that there was something wrong with the previous rename detection. But later on, we get a positive number of lines."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Conclusion\n",
"\n",
"So there are limitations of Git repository analysis when you don't want to dive deep into a more sophisticated model of the evolutions of a project.\n",
"\n",
"Here are some ideas to mitigate this problem around renames:\n",
"\n",
"1. Maybe more advanced Git repository mining tools: There are tools like the open-source tool [PyDriller](https://pydriller.readthedocs.io) or commercial tools like [CodeScence](https://codescene.io/) or [TeamScale](https://www.cqse.eu/en/products/teamscale/landing/) (from the later I know that they've invested significant brain-power to solve file renaming and merging problems)\n",
"\n",
"2. Leverage Git rename detection: Git provides rename detection by default. You might be able to tweak some parameters to get the results you need. I once used this but I can't remember any further details, though :-(\n",
"\n",
"3. Avoid file-based Git analysis: There are plenty of other interesting analyses waiting for you out there which could be more valuable in your specific context.\n",
"\n",
"4. Use the actual lines of code: You might use tools like `cloc` to get the real number of lines of your currently existing files in the repository.\n",
"\n",
"\n",
"As of today, I've chosen the latter two options (with a tendency to 3. ;-)).\n",
"\n",
"Using Git repository data together with the actual number of lines of code (option 4.) is good enough for me to get a first glimpse at the evolution of a software project.\n",
"\n",
"Your context could be a different one where you have to choose more sophisticated techniques to handle all the problems around Git analysis. It would be very interesting to get to know your specific context!\n",
"\n"
]
}
],
"metadata": {
"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.7.4"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
pandas
matplotlib
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment