git-pandas 2.1.0__tar.gz → 2.2.1__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.
Files changed (75) hide show
  1. {git_pandas-2.1.0 → git_pandas-2.2.1}/.github/workflows/docs.yml +8 -0
  2. {git_pandas-2.1.0 → git_pandas-2.2.1}/CHANGELOG.md +13 -0
  3. {git_pandas-2.1.0 → git_pandas-2.2.1}/LICENSE.md +1 -1
  4. {git_pandas-2.1.0 → git_pandas-2.2.1}/PKG-INFO +5 -2
  5. git_pandas-2.2.1/docs/source/cache.rst +121 -0
  6. {git_pandas-2.1.0 → git_pandas-2.2.1}/docs/source/project.rst +19 -15
  7. {git_pandas-2.1.0 → git_pandas-2.2.1}/docs/source/repository.rst +17 -2
  8. {git_pandas-2.1.0 → git_pandas-2.2.1}/docs/source/usecases.rst +48 -34
  9. {git_pandas-2.1.0 → git_pandas-2.2.1}/gitpandas/project.py +188 -162
  10. {git_pandas-2.1.0 → git_pandas-2.2.1}/gitpandas/repository.py +116 -32
  11. {git_pandas-2.1.0 → git_pandas-2.2.1}/pyproject.toml +8 -2
  12. git_pandas-2.1.0/docs/source/cache.rst +0 -74
  13. {git_pandas-2.1.0 → git_pandas-2.2.1}/.cursor/rules/project_overview.mdc +0 -0
  14. {git_pandas-2.1.0 → git_pandas-2.2.1}/.cursor/rules/pytest_standards.mdc +0 -0
  15. {git_pandas-2.1.0 → git_pandas-2.2.1}/.cursor/rules/python_standards.mdc +0 -0
  16. {git_pandas-2.1.0 → git_pandas-2.2.1}/.cursor/rules/sphinx_docs_standards.mdc +0 -0
  17. {git_pandas-2.1.0 → git_pandas-2.2.1}/.cursor/rules/testing.mdc +0 -0
  18. {git_pandas-2.1.0 → git_pandas-2.2.1}/.github/ISSUE_TEMPLATE.md +0 -0
  19. {git_pandas-2.1.0 → git_pandas-2.2.1}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  20. {git_pandas-2.1.0 → git_pandas-2.2.1}/.github/workflows/pypi-publish.yml +0 -0
  21. {git_pandas-2.1.0 → git_pandas-2.2.1}/.github/workflows/test-docs-build.yml +0 -0
  22. {git_pandas-2.1.0 → git_pandas-2.2.1}/.github/workflows/test-suite.yml +0 -0
  23. {git_pandas-2.1.0 → git_pandas-2.2.1}/.gitignore +0 -0
  24. {git_pandas-2.1.0 → git_pandas-2.2.1}/CONTRIBUTING.md +0 -0
  25. {git_pandas-2.1.0 → git_pandas-2.2.1}/Makefile +0 -0
  26. {git_pandas-2.1.0 → git_pandas-2.2.1}/README.md +0 -0
  27. {git_pandas-2.1.0 → git_pandas-2.2.1}/docs/Makefile +0 -0
  28. {git_pandas-2.1.0 → git_pandas-2.2.1}/docs/make.bat +0 -0
  29. {git_pandas-2.1.0 → git_pandas-2.2.1}/docs/requirements.txt +0 -0
  30. {git_pandas-2.1.0 → git_pandas-2.2.1}/docs/source/conf.py +0 -0
  31. {git_pandas-2.1.0 → git_pandas-2.2.1}/docs/source/contributors.rst +0 -0
  32. {git_pandas-2.1.0 → git_pandas-2.2.1}/docs/source/index.rst +0 -0
  33. {git_pandas-2.1.0 → git_pandas-2.2.1}/examples/__init__.py +0 -0
  34. {git_pandas-2.1.0 → git_pandas-2.2.1}/examples/attributes.py +0 -0
  35. {git_pandas-2.1.0 → git_pandas-2.2.1}/examples/bus_analysis.py +0 -0
  36. {git_pandas-2.1.0 → git_pandas-2.2.1}/examples/cloud_repo.py +0 -0
  37. {git_pandas-2.1.0 → git_pandas-2.2.1}/examples/commit_history.py +0 -0
  38. {git_pandas-2.1.0 → git_pandas-2.2.1}/examples/cumulative_blame.py +0 -0
  39. {git_pandas-2.1.0 → git_pandas-2.2.1}/examples/definitions.py +0 -0
  40. {git_pandas-2.1.0 → git_pandas-2.2.1}/examples/file_change_rates.py +0 -0
  41. {git_pandas-2.1.0 → git_pandas-2.2.1}/examples/hours_estimate.py +0 -0
  42. {git_pandas-2.1.0 → git_pandas-2.2.1}/examples/img/cumulative_blame.png +0 -0
  43. {git_pandas-2.1.0 → git_pandas-2.2.1}/examples/img/cumulative_project_blame.png +0 -0
  44. {git_pandas-2.1.0 → git_pandas-2.2.1}/examples/img/githubblame.png +0 -0
  45. {git_pandas-2.1.0 → git_pandas-2.2.1}/examples/img/lifeline.png +0 -0
  46. {git_pandas-2.1.0 → git_pandas-2.2.1}/examples/img/stravalib_cumulative_blame.png +0 -0
  47. {git_pandas-2.1.0 → git_pandas-2.2.1}/examples/lifeline.py +0 -0
  48. {git_pandas-2.1.0 → git_pandas-2.2.1}/examples/parallel_blame.py +0 -0
  49. {git_pandas-2.1.0 → git_pandas-2.2.1}/examples/project_blame.py +0 -0
  50. {git_pandas-2.1.0 → git_pandas-2.2.1}/examples/punchcard.py +0 -0
  51. {git_pandas-2.1.0 → git_pandas-2.2.1}/examples/repo_file_detail.py +0 -0
  52. {git_pandas-2.1.0 → git_pandas-2.2.1}/gitpandas/__init__.py +0 -0
  53. {git_pandas-2.1.0 → git_pandas-2.2.1}/gitpandas/cache.py +0 -0
  54. {git_pandas-2.1.0 → git_pandas-2.2.1}/gitpandas/utilities/__init__.py +0 -0
  55. {git_pandas-2.1.0 → git_pandas-2.2.1}/gitpandas/utilities/check_api.py +0 -0
  56. {git_pandas-2.1.0 → git_pandas-2.2.1}/gitpandas/utilities/plotting.py +0 -0
  57. {git_pandas-2.1.0 → git_pandas-2.2.1}/img/cumulative_blame.png +0 -0
  58. {git_pandas-2.1.0 → git_pandas-2.2.1}/img/lifeline.png +0 -0
  59. {git_pandas-2.1.0 → git_pandas-2.2.1}/img/punchcard.png +0 -0
  60. {git_pandas-2.1.0 → git_pandas-2.2.1}/tests/__init__.py +0 -0
  61. {git_pandas-2.1.0 → git_pandas-2.2.1}/tests/conftest.py +0 -0
  62. {git_pandas-2.1.0 → git_pandas-2.2.1}/tests/test_Project/__init__.py +0 -0
  63. {git_pandas-2.1.0 → git_pandas-2.2.1}/tests/test_Project/test_advanced.py +0 -0
  64. {git_pandas-2.1.0 → git_pandas-2.2.1}/tests/test_Project/test_properties.py +0 -0
  65. {git_pandas-2.1.0 → git_pandas-2.2.1}/tests/test_Repository/__init__.py +0 -0
  66. {git_pandas-2.1.0 → git_pandas-2.2.1}/tests/test_Repository/test_advanced.py +0 -0
  67. {git_pandas-2.1.0 → git_pandas-2.2.1}/tests/test_Repository/test_bus_factor.py +0 -0
  68. {git_pandas-2.1.0 → git_pandas-2.2.1}/tests/test_Repository/test_file_detail.py +0 -0
  69. {git_pandas-2.1.0 → git_pandas-2.2.1}/tests/test_Repository/test_properties.py +0 -0
  70. {git_pandas-2.1.0 → git_pandas-2.2.1}/tests/test_Repository/test_punchcard.py +0 -0
  71. {git_pandas-2.1.0 → git_pandas-2.2.1}/tests/test_cache.py +0 -0
  72. {git_pandas-2.1.0 → git_pandas-2.2.1}/tests/test_examples.py +0 -0
  73. {git_pandas-2.1.0 → git_pandas-2.2.1}/tests/test_utilities/test_check_api.py +0 -0
  74. {git_pandas-2.1.0 → git_pandas-2.2.1}/tests/test_utilities/test_init.py +0 -0
  75. {git_pandas-2.1.0 → git_pandas-2.2.1}/tests/test_utilities/test_plotting.py +0 -0
@@ -29,8 +29,16 @@ jobs:
29
29
  with:
30
30
  docs-folder: "./docs/"
31
31
 
32
+ - name: Debug directory structure
33
+ run: |
34
+ ls -la docs/
35
+ ls -la docs/build/ || echo "build dir not found"
36
+ ls -la docs/_build/ || echo "_build dir not found"
37
+
32
38
  - name: Create CNAME file
39
+ continue-on-error: true
33
40
  run: |
41
+ mkdir -p docs/build/html/
34
42
  echo "gitpandas.mcginniscommawill.com" > ./docs/build/html/CNAME
35
43
 
36
44
  - name: Deploy Docs
@@ -1,3 +1,16 @@
1
+ v2.2.1
2
+ ======
3
+
4
+ * Docs CI bugfix
5
+
6
+ v2.2.0
7
+ ======
8
+
9
+ * Support for default branch setting instead of assuming master, will infer if not passed
10
+ * Better handling of ignore repos in project directory setup
11
+ * Added a branch exists helper in repository
12
+ * Docs corrections
13
+
1
14
  v2.1.0
2
15
  ======
3
16
 
@@ -1,4 +1,4 @@
1
- Copyright (c) 2015, Helton Tech, LLC
1
+ Copyright (c) 2025, Will McGinnis
2
2
  All rights reserved.
3
3
 
4
4
  Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
@@ -1,8 +1,11 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: git-pandas
3
- Version: 2.1.0
3
+ Version: 2.2.1
4
4
  Summary: A utility for interacting with data from git repositories as Pandas dataframes
5
- Author-email: Will McGinnis <will@pedalwrencher.com>
5
+ Project-URL: Documentation, https://gitpandas.mcginniscommawill.com
6
+ Project-URL: Source, https://github.com/wdm0006/git-pandas
7
+ Project-URL: Issues, https://github.com/wdm0006/git-pandas/issues
8
+ Author: Will McGinnis
6
9
  License: BSD
7
10
  License-File: LICENSE.md
8
11
  Keywords: analysis,data,git,pandas
@@ -0,0 +1,121 @@
1
+ Caching
2
+ =======
3
+
4
+ Git-Pandas supports pluggable cache backends to optimize performance for expensive, repetitive operations. This is particularly useful for large repositories or when running multiple analyses.
5
+
6
+ Overview
7
+ --------
8
+
9
+ The caching system provides:
10
+ * In-memory caching for temporary results
11
+ * Redis-based caching for persistent storage
12
+ * Configurable cache durations
13
+ * Automatic cache invalidation
14
+ * Decorator-based caching for expensive operations
15
+
16
+ Available Cache Backends
17
+ ----------------------
18
+
19
+ In-Memory Cache (EphemeralCache)
20
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
21
+
22
+ The default in-memory cache is ephemeral and will be cleared when the process ends:
23
+
24
+ .. code-block:: python
25
+
26
+ from gitpandas import Repository
27
+ from gitpandas.cache import EphemeralCache
28
+
29
+ # Create an in-memory cache with default settings
30
+ cache = EphemeralCache()
31
+
32
+ # Or customize the cache size
33
+ cache = EphemeralCache(max_keys=500)
34
+
35
+ # Use the cache with a repository
36
+ repo = Repository('/path/to/repo', cache_backend=cache)
37
+
38
+ Redis Cache (RedisDFCache)
39
+ ~~~~~~~~~~~~~~~~~~~~~~~
40
+
41
+ For persistent caching across sessions, use Redis:
42
+
43
+ .. code-block:: python
44
+
45
+ from gitpandas import Repository
46
+ from gitpandas.cache import RedisDFCache
47
+
48
+ # Create a Redis cache with default settings
49
+ cache = RedisDFCache()
50
+
51
+ # Or customize Redis connection and cache settings
52
+ cache = RedisDFCache(
53
+ host='localhost',
54
+ port=6379,
55
+ db=12,
56
+ max_keys=1000,
57
+ ttl=3600 # Cache entries expire after 1 hour
58
+ )
59
+
60
+ # Use the cache with a repository
61
+ repo = Repository('/path/to/repo', cache_backend=cache)
62
+
63
+ Using the Cache Decorator
64
+ ----------------------
65
+
66
+ The `@multicache` decorator can be used to cache method results:
67
+
68
+ .. code-block:: python
69
+
70
+ from gitpandas.cache import multicache
71
+
72
+ @multicache(
73
+ key_prefix="method_name",
74
+ key_list=["param1", "param2"],
75
+ skip_if=lambda x: x.get("param1") is None
76
+ )
77
+ def expensive_method(self, param1, param2):
78
+ # Method implementation
79
+ pass
80
+
81
+ Configuration
82
+ ------------
83
+
84
+ Cache backends can be configured with various parameters:
85
+
86
+ EphemeralCache:
87
+ * ``max_keys``: Maximum number of keys to store in memory (default: 1000)
88
+
89
+ RedisDFCache:
90
+ * ``host``: Redis host (default: 'localhost')
91
+ * ``port``: Redis port (default: 6379)
92
+ * ``db``: Redis database number (default: 12)
93
+ * ``max_keys``: Maximum number of keys to store (default: 1000)
94
+ * ``ttl``: Time-to-live in seconds for cache entries (default: None, no expiration)
95
+ * Additional keyword arguments are passed to redis.StrictRedis
96
+
97
+ API Reference
98
+ ------------
99
+
100
+ .. currentmodule:: gitpandas.cache
101
+
102
+ .. autoclass:: CacheBackend
103
+ :members:
104
+ :undoc-members:
105
+ :show-inheritance:
106
+ :inherited-members:
107
+ :special-members: __init__, __getitem__, __setitem__, __delitem__, __contains__, __len__
108
+
109
+ .. autoclass:: EphemeralCache
110
+ :members:
111
+ :undoc-members:
112
+ :show-inheritance:
113
+ :inherited-members:
114
+
115
+ .. autoclass:: RedisDFCache
116
+ :members:
117
+ :undoc-members:
118
+ :show-inheritance:
119
+ :inherited-members:
120
+
121
+ .. autofunction:: multicache
@@ -26,15 +26,20 @@ Create a ProjectDirectory from a directory containing multiple repositories:
26
26
  .. code-block:: python
27
27
 
28
28
  from gitpandas import ProjectDirectory
29
- project = ProjectDirectory(working_dir='/path/to/dir/', ignore=None, verbose=True)
29
+ project = ProjectDirectory(
30
+ working_dir='/path/to/dir/',
31
+ ignore_repos=['repo_to_ignore'],
32
+ verbose=True,
33
+ default_branch='main' # Optional, will auto-detect if not specified
34
+ )
30
35
 
31
- The `ignore` parameter can be a list of directories to exclude. This method uses `os.walk` to search for `.git` directories recursively.
36
+ The `ignore_repos` parameter can be a list of repository names to exclude. This method uses `os.walk` to search for `.git` directories recursively.
32
37
 
33
38
  To check which repositories are included:
34
39
 
35
40
  .. code-block:: python
36
41
 
37
- print(project._repo_name())
42
+ print(project.repo_name())
38
43
 
39
44
  Explicit Local Repositories
40
45
  ~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -46,8 +51,9 @@ Create a ProjectDirectory from a list of local repositories:
46
51
  from gitpandas import ProjectDirectory
47
52
  project = ProjectDirectory(
48
53
  working_dir=['/path/to/repo1/', '/path/to/repo2/'],
49
- ignore=None,
50
- verbose=True
54
+ ignore_repos=['repo_to_ignore'],
55
+ verbose=True,
56
+ default_branch='main' # Optional, will auto-detect if not specified
51
57
  )
52
58
 
53
59
  Explicit Remote Repositories
@@ -60,8 +66,9 @@ Create a ProjectDirectory from a list of remote repositories:
60
66
  from gitpandas import ProjectDirectory
61
67
  project = ProjectDirectory(
62
68
  working_dir=['git://github.com/user/repo1.git', 'git://github.com/user/repo2.git'],
63
- ignore=None,
64
- verbose=True
69
+ ignore_repos=['repo_to_ignore'],
70
+ verbose=True,
71
+ default_branch='main' # Optional, will auto-detect if not specified
65
72
  )
66
73
 
67
74
  Common Operations
@@ -72,7 +79,7 @@ Here are some common operations you can perform with a ProjectDirectory object:
72
79
  .. code-block:: python
73
80
 
74
81
  # Get commit history across all repositories
75
- commits_df = project.commit_history(branch='master')
82
+ commits_df = project.commit_history()
76
83
 
77
84
  # Get blame information across all repositories
78
85
  blame_df = project.blame()
@@ -85,6 +92,9 @@ Here are some common operations you can perform with a ProjectDirectory object:
85
92
 
86
93
  # Get file change history across all repositories
87
94
  changes_df = project.file_change_history()
95
+
96
+ # Get detailed repository information
97
+ repo_info = project.repo_information()
88
98
 
89
99
  API Reference
90
100
  ------------
@@ -110,11 +120,5 @@ API Reference
110
120
  ~ProjectDirectory.tags
111
121
  ~ProjectDirectory.revs
112
122
  ~ProjectDirectory.bus_factor
113
- ~ProjectDirectory.is_bare
114
- ~ProjectDirectory.has_coverage
115
-
116
- .. rubric:: Properties
117
-
118
- .. autosummary::
119
- :nosignatures:
123
+ ~ProjectDirectory.repo_information
120
124
  ~ProjectDirectory.repo_name
@@ -26,7 +26,11 @@ Create a Repository from a local Git repository:
26
26
  .. code-block:: python
27
27
 
28
28
  from gitpandas import Repository
29
- repo = Repository(working_dir='/path/to/repo/', verbose=True)
29
+ repo = Repository(
30
+ working_dir='/path/to/repo/',
31
+ verbose=True,
32
+ default_branch='main' # Optional, will auto-detect if not specified
33
+ )
30
34
 
31
35
  The directory must contain a `.git` directory. Subdirectories are not searched.
32
36
 
@@ -38,7 +42,11 @@ Create a Repository from a remote Git repository:
38
42
  .. code-block:: python
39
43
 
40
44
  from gitpandas import Repository
41
- repo = Repository(working_dir='git://github.com/user/repo.git', verbose=True)
45
+ repo = Repository(
46
+ working_dir='git://github.com/user/repo.git',
47
+ verbose=True,
48
+ default_branch='main' # Optional, will auto-detect if not specified
49
+ )
42
50
 
43
51
  The repository will be cloned locally into a temporary directory. This can be slow for large repositories.
44
52
 
@@ -63,6 +71,9 @@ Here are some common operations you can perform with a Repository object:
63
71
 
64
72
  # Get file change history
65
73
  changes_df = repo.file_change_history()
74
+
75
+ # Get repository name
76
+ repo_name = repo.repo_name
66
77
 
67
78
  API Reference
68
79
  ------------
@@ -90,12 +101,16 @@ API Reference
90
101
  ~Repository.bus_factor
91
102
  ~Repository.is_bare
92
103
  ~Repository.has_coverage
104
+ ~Repository.has_branch
105
+ ~Repository.get_branches_by_commit
106
+ ~Repository.commits_in_tags
93
107
 
94
108
  .. rubric:: Properties
95
109
 
96
110
  .. autosummary::
97
111
  :nosignatures:
98
112
  ~Repository.repo_name
113
+ ~Repository.default_branch
99
114
 
100
115
  .. autoclass:: GitFlowRepository
101
116
  :members:
@@ -17,7 +17,7 @@ Get basic information about a repository:
17
17
  repo = Repository('/path/to/repo')
18
18
 
19
19
  # Get repository name
20
- print(repo._repo_name())
20
+ print(repo.repo_name)
21
21
 
22
22
  # Check if repository is bare
23
23
  print(repo.is_bare())
@@ -52,6 +52,9 @@ Analyze commit patterns and history:
52
52
 
53
53
  # Filter by directory
54
54
  src_changes = repo.file_change_history(include_globs=['src/*'])
55
+
56
+ # Get commits in tags
57
+ tag_commits = repo.commits_in_tags()
55
58
 
56
59
  Project-Level Analysis
57
60
  --------------------
@@ -71,17 +74,17 @@ Analyze multiple repositories simultaneously:
71
74
  'git://github.com/user/repo2.git'
72
75
  ])
73
76
 
74
- # Get aggregated metrics
75
- print(project.general_information())
77
+ # Get repository information
78
+ print(project.repo_information())
76
79
 
77
80
  # Calculate bus factor
78
81
  print(project.bus_factor())
79
82
 
80
- # Get file change rates
81
- print(project.file_change_rates())
83
+ # Get file change history
84
+ print(project.file_change_history())
82
85
 
83
- # Generate punchcard data
84
- print(project.punchcard())
86
+ # Get blame information
87
+ print(project.blame())
85
88
 
86
89
  Advanced Analysis
87
90
  ---------------
@@ -96,7 +99,7 @@ Track code ownership over time:
96
99
  # Get cumulative blame
97
100
  blame_df = repo.cumulative_blame()
98
101
 
99
- # Plot cumulative blame
102
+ # Plot cumulative blame using pandas plotting
100
103
  import matplotlib.pyplot as plt
101
104
  blame_df.plot(x='date', y='loc', title='Cumulative Blame Over Time')
102
105
  plt.show()
@@ -108,14 +111,14 @@ Analyze project sustainability:
108
111
 
109
112
  .. code-block:: python
110
113
 
111
- # Calculate bus factor
112
- bus_factor = project.bus_factor()
114
+ # Calculate bus factor for repository
115
+ bus_factor = repo.bus_factor()
113
116
 
114
- # Get detailed contributor metrics
115
- contributors_df = project.contributor_metrics()
117
+ # Get detailed blame information
118
+ blame_df = repo.blame(by='file') # Get file-level blame details
116
119
 
117
- # Analyze file ownership
118
- ownership_df = project.file_ownership()
120
+ # Analyze ownership patterns
121
+ ownership_patterns = repo.blame(committer=True, by='repository')
119
122
 
120
123
  Performance Optimization
121
124
  ---------------------
@@ -127,33 +130,40 @@ Optimize performance with caching:
127
130
 
128
131
  .. code-block:: python
129
132
 
130
- # Enable in-memory caching
131
- repo = Repository('/path/to/repo', cache=True)
132
-
133
- # Use Redis for persistent caching
134
- repo = Repository(
135
- '/path/to/repo',
136
- cache=True,
137
- cache_backend='redis',
138
- redis_url='redis://localhost:6379/0'
133
+ from gitpandas import Repository
134
+ from gitpandas.cache import EphemeralCache, RedisDFCache
135
+
136
+ # Use in-memory caching
137
+ cache = EphemeralCache()
138
+ repo = Repository('/path/to/repo', cache_backend=cache)
139
+
140
+ # Or use Redis for persistent caching
141
+ redis_cache = RedisDFCache(
142
+ host='localhost',
143
+ port=6379,
144
+ db=12,
145
+ ttl=3600 # Cache entries expire after 1 hour
139
146
  )
147
+ repo = Repository('/path/to/repo', cache_backend=redis_cache)
140
148
 
141
149
  Visualization Examples
142
150
  -------------------
143
151
 
144
- Commit Patterns
152
+ Commit Analysis
145
153
  ~~~~~~~~~~~~~
146
154
 
147
155
  Visualize commit patterns:
148
156
 
149
157
  .. code-block:: python
150
158
 
151
- # Generate punchcard data
152
- punchcard_df = repo.punchcard()
159
+ # Get commit history
160
+ commit_df = repo.commit_history()
153
161
 
154
- # Plot commit patterns
155
- import matplotlib.pyplot as plt
156
- punchcard_df.plot(kind='heatmap', title='Commit Patterns')
162
+ # Plot commits over time using pandas
163
+ commit_df.resample('D').size().plot(
164
+ kind='bar',
165
+ title='Commits per Day'
166
+ )
157
167
  plt.show()
158
168
 
159
169
  File Change Analysis
@@ -166,17 +176,21 @@ Visualize file changes:
166
176
  # Get file change history
167
177
  changes_df = repo.file_change_history()
168
178
 
169
- # Plot changes over time
170
- changes_df.plot(x='date', y='changes', title='File Changes Over Time')
179
+ # Plot changes over time using pandas
180
+ changes_df.groupby('filename')['insertions'].sum().plot(
181
+ kind='bar',
182
+ title='Lines Added by File'
183
+ )
171
184
  plt.show()
172
185
 
173
186
  Best Practices
174
187
  ------------
175
188
 
176
- * Use caching for expensive operations
177
- * Filter data early to improve performance
189
+ * Use caching for expensive operations like blame analysis
190
+ * Filter data early using include_globs/ignore_globs
178
191
  * Leverage pandas operations for analysis
179
192
  * Consider memory usage with large repositories
180
- * Use appropriate visualization tools
193
+ * Use appropriate branch names (main/master)
194
+ * Handle repository cleanup properly when using remote repositories
181
195
 
182
196
  For more examples and detailed API documentation, see the :doc:`repository` and :doc:`project` pages.