better-git-of-theseus 0.4.2__tar.gz → 0.4.5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {better_git_of_theseus-0.4.2 → better_git_of_theseus-0.4.5}/PKG-INFO +1 -1
- {better_git_of_theseus-0.4.2 → better_git_of_theseus-0.4.5}/better_git_of_theseus.egg-info/PKG-INFO +1 -1
- {better_git_of_theseus-0.4.2 → better_git_of_theseus-0.4.5}/git_of_theseus/app.py +88 -8
- {better_git_of_theseus-0.4.2 → better_git_of_theseus-0.4.5}/git_of_theseus/cmd.py +2 -3
- {better_git_of_theseus-0.4.2 → better_git_of_theseus-0.4.5}/git_of_theseus/plotly_plots.py +33 -0
- {better_git_of_theseus-0.4.2 → better_git_of_theseus-0.4.5}/setup.py +1 -1
- {better_git_of_theseus-0.4.2 → better_git_of_theseus-0.4.5}/LICENSE +0 -0
- {better_git_of_theseus-0.4.2 → better_git_of_theseus-0.4.5}/README.md +0 -0
- {better_git_of_theseus-0.4.2 → better_git_of_theseus-0.4.5}/better_git_of_theseus.egg-info/SOURCES.txt +0 -0
- {better_git_of_theseus-0.4.2 → better_git_of_theseus-0.4.5}/better_git_of_theseus.egg-info/dependency_links.txt +0 -0
- {better_git_of_theseus-0.4.2 → better_git_of_theseus-0.4.5}/better_git_of_theseus.egg-info/entry_points.txt +0 -0
- {better_git_of_theseus-0.4.2 → better_git_of_theseus-0.4.5}/better_git_of_theseus.egg-info/requires.txt +0 -0
- {better_git_of_theseus-0.4.2 → better_git_of_theseus-0.4.5}/better_git_of_theseus.egg-info/top_level.txt +0 -0
- {better_git_of_theseus-0.4.2 → better_git_of_theseus-0.4.5}/git_of_theseus/__init__.py +0 -0
- {better_git_of_theseus-0.4.2 → better_git_of_theseus-0.4.5}/git_of_theseus/analyze.py +0 -0
- {better_git_of_theseus-0.4.2 → better_git_of_theseus-0.4.5}/git_of_theseus/line_plot.py +0 -0
- {better_git_of_theseus-0.4.2 → better_git_of_theseus-0.4.5}/git_of_theseus/stack_plot.py +0 -0
- {better_git_of_theseus-0.4.2 → better_git_of_theseus-0.4.5}/git_of_theseus/survival_plot.py +0 -0
- {better_git_of_theseus-0.4.2 → better_git_of_theseus-0.4.5}/git_of_theseus/utils.py +0 -0
- {better_git_of_theseus-0.4.2 → better_git_of_theseus-0.4.5}/setup.cfg +0 -0
|
@@ -4,13 +4,26 @@ import tempfile
|
|
|
4
4
|
import shutil
|
|
5
5
|
try:
|
|
6
6
|
from git_of_theseus.analyze import analyze
|
|
7
|
-
from git_of_theseus.plotly_plots import plotly_stack_plot, plotly_line_plot, plotly_survival_plot
|
|
7
|
+
from git_of_theseus.plotly_plots import plotly_stack_plot, plotly_line_plot, plotly_survival_plot, plotly_bar_plot
|
|
8
8
|
except ImportError:
|
|
9
9
|
from analyze import analyze
|
|
10
|
-
from plotly_plots import plotly_stack_plot, plotly_line_plot, plotly_survival_plot
|
|
10
|
+
from plotly_plots import plotly_stack_plot, plotly_line_plot, plotly_survival_plot, plotly_bar_plot
|
|
11
11
|
|
|
12
12
|
st.set_page_config(page_title="Git of Theseus Dash", layout="wide")
|
|
13
13
|
|
|
14
|
+
# GitHub Link in Sidebar
|
|
15
|
+
st.sidebar.markdown(
|
|
16
|
+
"""
|
|
17
|
+
<div style="display: flex; align-items: center; margin-bottom: 20px;">
|
|
18
|
+
<img src="https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png" width="30" style="margin-right: 10px;">
|
|
19
|
+
<a href="https://github.com/onewesong/better-git-of-theseus" target="_blank" style="text-decoration: none; color: inherit; font-weight: bold;">
|
|
20
|
+
better-git-of-theseus
|
|
21
|
+
</a>
|
|
22
|
+
</div>
|
|
23
|
+
""",
|
|
24
|
+
unsafe_allow_html=True
|
|
25
|
+
)
|
|
26
|
+
|
|
14
27
|
st.title("📊 Git of Theseus - Repository Analysis")
|
|
15
28
|
|
|
16
29
|
import sys
|
|
@@ -18,12 +31,49 @@ import sys
|
|
|
18
31
|
# Sidebar Configuration
|
|
19
32
|
st.sidebar.header("Configuration")
|
|
20
33
|
|
|
34
|
+
with st.sidebar.expander("📖 How to use", expanded=False):
|
|
35
|
+
st.markdown("""
|
|
36
|
+
**Better Git of Theseus** is a tool to analyze the evolution of Git repositories.
|
|
37
|
+
|
|
38
|
+
### Plots Explained:
|
|
39
|
+
- **Stack Plot**: Shows code growth over time, broken down by cohort (when code was added).
|
|
40
|
+
- **Line Plot**: Shows trends across different dimensions (Author, Extension, etc.).
|
|
41
|
+
- **Distribution**: Shows the **current** distribution (Who contributed most, which file types are dominant).
|
|
42
|
+
- **Survival Plot**: Estimates how long a line of code typically lasts before being modified or deleted.
|
|
43
|
+
|
|
44
|
+
### Tips:
|
|
45
|
+
- **Cohort Format**: `%Y` (Yearly) and `%Y-%m` (Monthly) are recommended.
|
|
46
|
+
- **Mailmap**: Use a `.mailmap` file in the repo root to resolve duplicate author names.
|
|
47
|
+
""")
|
|
48
|
+
|
|
21
49
|
default_repo = "."
|
|
22
50
|
if len(sys.argv) > 1:
|
|
23
51
|
default_repo = sys.argv[1]
|
|
24
52
|
|
|
25
|
-
repo_path =
|
|
26
|
-
|
|
53
|
+
repo_path = default_repo
|
|
54
|
+
# Path display removed as per user request
|
|
55
|
+
|
|
56
|
+
# Fetch branches for the selectbox
|
|
57
|
+
try:
|
|
58
|
+
import git
|
|
59
|
+
repo = git.Repo(repo_path)
|
|
60
|
+
# Get local branches
|
|
61
|
+
branches = [h.name for h in repo.heads]
|
|
62
|
+
|
|
63
|
+
# Try to determine the best default branch (active one, or master/main)
|
|
64
|
+
try:
|
|
65
|
+
current_active = repo.active_branch.name
|
|
66
|
+
except:
|
|
67
|
+
current_active = "master"
|
|
68
|
+
|
|
69
|
+
if current_active in branches:
|
|
70
|
+
branches.remove(current_active)
|
|
71
|
+
|
|
72
|
+
options = [current_active] + sorted(branches)
|
|
73
|
+
branch = st.sidebar.selectbox("Branch", options=options)
|
|
74
|
+
except Exception as e:
|
|
75
|
+
# Fallback if git repo access fails
|
|
76
|
+
branch = st.sidebar.text_input("Branch", value="master")
|
|
27
77
|
|
|
28
78
|
with st.sidebar.expander("Analysis Parameters"):
|
|
29
79
|
cohortfm = st.text_input(
|
|
@@ -35,9 +85,23 @@ with st.sidebar.expander("Analysis Parameters"):
|
|
|
35
85
|
"- `%Y-W%W`: Week (e.g., 2023-W01)\n"
|
|
36
86
|
"- `%Y-%m-%d`: Day"
|
|
37
87
|
)
|
|
38
|
-
interval = st.number_input(
|
|
39
|
-
|
|
40
|
-
|
|
88
|
+
interval = st.number_input(
|
|
89
|
+
"Analysis Interval (seconds)",
|
|
90
|
+
value=7 * 24 * 60 * 60,
|
|
91
|
+
help="The time step between data points. Default is 604800s (7 days). Larger values are faster; smaller values result in smoother curves."
|
|
92
|
+
)
|
|
93
|
+
st.caption(f"Current resolution: {interval / 86400:.1f} days")
|
|
94
|
+
|
|
95
|
+
procs = st.number_input(
|
|
96
|
+
"Parallel Processes",
|
|
97
|
+
value=2,
|
|
98
|
+
min_value=1,
|
|
99
|
+
help="Number of concurrent processes. Increase to speed up analysis on multi-core CPUs, but note it increases RAM usage."
|
|
100
|
+
)
|
|
101
|
+
ignore = st.text_area(
|
|
102
|
+
"Ignore Patterns",
|
|
103
|
+
help="Glob patterns to ignore (comma separated), e.g.: 'tests/**, *.md'"
|
|
104
|
+
).split(",")
|
|
41
105
|
ignore = [i.strip() for i in ignore if i.strip()]
|
|
42
106
|
|
|
43
107
|
@st.cache_data(show_spinner=False)
|
|
@@ -71,7 +135,7 @@ if st.sidebar.button("🚀 Run Analysis") or (len(sys.argv) > 1 and st.session_s
|
|
|
71
135
|
# Main View
|
|
72
136
|
if st.session_state.analysis_results:
|
|
73
137
|
results = st.session_state.analysis_results
|
|
74
|
-
tab1, tab2, tab3 = st.tabs(["Stack Plot", "Line Plot", "Survival Plot"])
|
|
138
|
+
tab1, tab2, tab3, tab4 = st.tabs(["Stack Plot", "Line Plot", "Distribution", "Survival Plot"])
|
|
75
139
|
|
|
76
140
|
with tab1:
|
|
77
141
|
st.header("Stack Plot")
|
|
@@ -115,6 +179,22 @@ if st.session_state.analysis_results:
|
|
|
115
179
|
st.warning(f"Data for {data_source_label_line} not found.")
|
|
116
180
|
|
|
117
181
|
with tab3:
|
|
182
|
+
st.header("Latest Distribution")
|
|
183
|
+
col1, col2 = st.columns([1, 3])
|
|
184
|
+
with col1:
|
|
185
|
+
data_source_label_bar = st.selectbox("Data Source", list(source_map.keys()), key="bar_source")
|
|
186
|
+
data_key_bar = source_map[data_source_label_bar]
|
|
187
|
+
max_n_bar = st.slider("Max Series", 5, 100, 30, key="bar_max_n")
|
|
188
|
+
with col2:
|
|
189
|
+
project_name = os.path.basename(os.path.abspath(repo_path))
|
|
190
|
+
data_bar = results.get(data_key_bar)
|
|
191
|
+
if data_bar:
|
|
192
|
+
fig = plotly_bar_plot(data_bar, max_n=max_n_bar, title=f"{project_name} - {data_source_label_bar}")
|
|
193
|
+
st.plotly_chart(fig, width="stretch")
|
|
194
|
+
else:
|
|
195
|
+
st.warning(f"Data for {data_source_label_bar} not found.")
|
|
196
|
+
|
|
197
|
+
with tab4:
|
|
118
198
|
st.header("Survival Plot")
|
|
119
199
|
col1, col2 = st.columns([1, 3])
|
|
120
200
|
with col1:
|
|
@@ -7,9 +7,8 @@ def main():
|
|
|
7
7
|
cmd_dir = os.path.dirname(os.path.abspath(__file__))
|
|
8
8
|
app_path = os.path.join(cmd_dir, "app.py")
|
|
9
9
|
|
|
10
|
-
#
|
|
11
|
-
repo_path =
|
|
12
|
-
repo_path = os.path.abspath(repo_path)
|
|
10
|
+
# Always use the current working directory
|
|
11
|
+
repo_path = os.path.abspath(os.getcwd())
|
|
13
12
|
|
|
14
13
|
# Run streamlit
|
|
15
14
|
# We pass the repo_path as an argument to the streamlit script
|
|
@@ -240,4 +240,37 @@ def plotly_survival_plot(commit_history, exp_fit=False, years=5, title=None):
|
|
|
240
240
|
)
|
|
241
241
|
|
|
242
242
|
|
|
243
|
+
return fig
|
|
244
|
+
|
|
245
|
+
def plotly_bar_plot(data, max_n=20, title=None):
|
|
246
|
+
ts, y, labels = _process_stack_line_data(data, max_n, normalize=False)
|
|
247
|
+
|
|
248
|
+
# Get latest data point (current state)
|
|
249
|
+
latest_values = [row[-1] for row in y]
|
|
250
|
+
|
|
251
|
+
# Sort by value for better bar chart presentation
|
|
252
|
+
# (Though _process_stack_line_data already does some sorting, we want descending order)
|
|
253
|
+
indices = sorted(range(len(labels)), key=lambda i: latest_values[i], reverse=True)
|
|
254
|
+
|
|
255
|
+
sorted_labels = [labels[i] for i in indices]
|
|
256
|
+
sorted_values = [latest_values[i] for i in indices]
|
|
257
|
+
|
|
258
|
+
# Generate colors
|
|
259
|
+
colors = px.colors.qualitative.Plotly
|
|
260
|
+
if len(sorted_labels) > len(colors):
|
|
261
|
+
colors = px.colors.qualitative.Dark24
|
|
262
|
+
|
|
263
|
+
fig = go.Figure(go.Bar(
|
|
264
|
+
x=sorted_labels,
|
|
265
|
+
y=sorted_values,
|
|
266
|
+
marker_color=[colors[i % len(colors)] for i in range(len(sorted_labels))]
|
|
267
|
+
))
|
|
268
|
+
|
|
269
|
+
fig.update_layout(
|
|
270
|
+
title=dict(text=f"{title} (Current Distribution)" if title else "Current Distribution", x=0.5),
|
|
271
|
+
yaxis=dict(title="Lines of Code"),
|
|
272
|
+
xaxis=dict(title=""),
|
|
273
|
+
margin=dict(l=20, r=20, t=50, b=100),
|
|
274
|
+
)
|
|
275
|
+
|
|
243
276
|
return fig
|
|
@@ -5,7 +5,7 @@ with open("README.md", "r", encoding="utf-8") as fh:
|
|
|
5
5
|
|
|
6
6
|
setup(
|
|
7
7
|
name="better-git-of-theseus",
|
|
8
|
-
version="0.4.
|
|
8
|
+
version="0.4.5",
|
|
9
9
|
description="Plot stats on Git repositories with interactive Plotly charts",
|
|
10
10
|
long_description=long_description,
|
|
11
11
|
long_description_content_type="text/markdown",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|