René's URL Explorer Experiment
Title: Fix version_info cache invalidation, typing, parsing, and serialization by EliahKagan · Pull Request #1838 · gitpython-developers/GitPython · GitHub
Open Graph Title: Fix version_info cache invalidation, typing, parsing, and serialization by EliahKagan · Pull Request #1838 · gitpython-developers/GitPython
X Title: Fix version_info cache invalidation, typing, parsing, and serialization by EliahKagan · Pull Request #1838 · gitpython-developers/GitPython
Description: Fixes #1829 - cache invalidation
Fixes #1830 - typing
Fixes #1833 - parsing
Fixes #1836 - serialization
Much of the design in the fixes for #1830, #1833, and #1836, as well as why I think it makes sense to include them here with #1829, is already largely covered in the discussion/comments in those three issues.
Design goals for #1829
For #1829, which I would regard to be the biggest of the changes here, and which is the change for which the most additional code is added to the test suite, I made sure to preserve three important properties of the code:
Refreshing is global but affects all Git instances. Changing the way refreshing itself fundamentally works is beyond the scope of this PR and would also be a breaking change that I think could only be made in a new major release, and there are good reasons for refreshing to be global. Allowing a per-instance overriding refresh might be useful, but would be much more complicated than the current system unless accompanied by other design changes that would likely be breaking.
Caching is performed for version_info attributes. This has been explicitly promised in the version_info docstring for a long time, with the intention that accessing version_info be understood as cheap, and it is likely that substantial code exists that assumes it is cheap (or that throttles parts of GitPython that use it and benefit from it being cheap).
Caching does not take place across Git instances, and accessing version_info on one Git instance never has any effect on what happens when version_info is accessed on another Git instance. Since the actual validity of cached version information depends on when the global operation of refreshing was last done, it may seem like the caching should be made global as well. But this can lead to some new problems, such that I suggest it either be put off until later or never done.
Why not cache globally?
To expand on why the that third property is something I've taken care to preserve, these are the three problems I anticipate will arise if the caching were to be made global, in increasing order of significance:
Although GitPython is not in general thread-safe (#1052), caching globally may still make it less capable of concurrency than before, and the interaction with multiprocessing would also have to be considered.
That the cache is per-instance is a long-standing behavior, which existing code may rely on. It is not explicitly stated in the property docstring, and I've taken care not to start stating it, in case it is to be changed. But I think it is how readers would have understood the docstring, since it is documenting an instance property and does not describe the cache as global or shared.
Not sharing the cache ensures that the code Git().version_info always makes a subprocess call to get fresh information. Besides being one of the ways this behavior may have been relied on already, changing it would also, in practice, lead to code that uses GitPython being written to refresh more often, which would have the opposite effect on speed and resource usage than is intended by caching.
The cache invalidation implementation
Rather than having the refresh operation reach out to all Git instances to invalidate their caches, I've added a _refresh_token class attribute that is set and reset along with GIT_PYTHON_GIT_EXECUTABLE during a refresh, and added a _version_info_token instance attribute (that, like _version_info, is excluded from pickle serialization).
When version_info is accessed, if _version_info_token is the same object as _refresh_token, then the cache is valid and the cached value in _version_info is returned. Otherwise, the cache is invalid, and git version is run to obtain the version; the version information is cached in _version_info, _version_info_token is updated to _refresh_token, and the newly computed value is returned.
The _version_info_token instance attribute starts out as None to simplify debugging and unpickling. Its value is always either None or a value that has at some point been the value of the _refresh_token class attribute. The _refresh_token class attribute is never None, but always a direct instance of object, even before the first refresh, which ensures it never wrongly matches the initial None value of a _version_info_token instance attribute.
A successful refresh always causes _refresh_token to differ from any _version_info_token that exists at that time. This is important--and, in particular, these tokens must not be tied to the value of Git.GIT_PYTHON_GIT_EXECUTABLE--because refreshing must properly update for a change to which git implementation is accessed by the same command or even via the same path. The most common and important such case is probably when GIT_PYTHON_GIT_EXECUTABLE is the default value of "git", and git has been upgraded or a new version installed in a $PATH directory.
Dropping the LazyMixin base class
An aspect of this PR that I recommend be double-checked in review is my assumption that Git having LazyMixin as a base class can be considered a mere implementation detail of Git, and thus that it is non-breaking to remove inheritance from that base class.
I removed it because it was only being used to implement caching for the version_info property. It is well-suited to some forms of caching elsewhere in GitPython, but not well-suited to the changes needed in version_info for #1829 and #1836 (as discussed in #1836). Because I stopped using LazyMixin for this, it was no longer used for anything. Removing it simplified the code, though if necessary it could be brought back and just not used for anything. (The new version_info implementation would still work with LazyMixin present but unused.)
Other considerations
Keeping the super().__init__ call
Git.__init__ contains this line:
GitPython/git/cmd.py
Line 778
in
afa5754
super().__init__()
This superficially appears related to the use of LazyMixin, but I believe that is not the case. LazyMixin, which is provided by the gitdb dependency, at least currently does not define an __init__ method. Furthermore, such a super() call was present even before LazyMixin was brought in as a base class in e583a9e to provide caching for the version_info property (which was also introduced at that time). At that time, Git had no base class besides object, yet this was still present.
Calling super().__init__() helps with cooperative multiple inheritance in some cases. It is sometimes done even in the absence of a specific need, though sometimes it's not the correct logic, if the next class in the MRO has an __init__ that is not callable with no arguments (or, rather, with only a self argument). Furthermore, you've given feedback that I have understood to mean that having Git support inheritance is not a design priority.
So I don't think this super().__init__() is present based on a general preference for it. Instead, looking back to when it was first introduced, this was in the first commit that had the Git class, 039bfe3. At that time, Git did have a base class, MethodMissingMixin.
It may be that it should either be removed or commented with an explanation. But I think it is sufficiently unconnected to LazyMixin (and version_info) that no change should be made to it in connection with the changes in this PR.
Effect of directly accessing _version_info has changed
When LazyMixin was used, reading from the nonpublic _version_info attribute behaved the same as reading from version_info. Code outside the Git class should not do that, but there is one place in GitPython's own tests where it did:
GitPython/test/test_index.py
Line 531
in
afa5754
if rw_repo.git._version_info < (2, 29):
That had to be changed to use version_info instead. If necessary, a different backing attribute could be used, and _version_info could be made an undocumented alternative to version_info that issues a warning but otherwise behaves the same. However, it seems to me that this is not necessary; _version_info was never advertised as part of the public interface of Git, as far as I can tell.
GIT_PYTHON_GIT_EXECUTABLE as an instance attribute is not supported
If Git instances could have their own GIT_PYTHON_GIT_EXECUTABLE attributes, then they would be used instead of the same-named class attribute that is written in a refresh. This cannot happen on a direct Git instance, because Git is slotted and has no such slot (and cannot, since the descriptor would conflict with the class attribute). But it is possible to make a subclass of Git where GIT_PYTHON_GIT_EXECUTABLE is an instance attribute. This has never been supported and should not be done, and will likely work poorly in other ways, but it is possible for caching to "support" it by detecting that this is the case and just never caching version_info.
I have neither done this nor commented about it, and even this mention of it may be gratuitous. However, in general the effect of attempting to set what is assumed to be a class attribute on an instance should be considered. My reasoning for not dealing with this at all is that, even if a reasonable use case arises to derive from Git and shadow base-class class attributes as instance attributes (which seems unlikely), the onus is then on the author of the derived class to override base-class members like version_info accordingly.
Open Graph Description: Fixes #1829 - cache invalidation
Fixes #1830 - typing
Fixes #1833 - parsing
Fixes #1836 - serialization
Much of the design in the fixes for #1830, #1833, and #1836, as well as why I think it makes ...
X Description: Fixes #1829 - cache invalidation
Fixes #1830 - typing
Fixes #1833 - parsing
Fixes #1836 - serialization
Much of the design in the fixes for #1830, #1833, and #1836, as well as why I think it makes ...
Opengraph URL: https://github.com/gitpython-developers/GitPython/pull/1838
X: @github
direct link
Domain: github.com
| route-pattern | /:user_id/:repository/pull/:id/files(.:format) |
| route-controller | pull_requests |
| route-action | files |
| fetch-nonce | v2:ff907d05-cb4a-c6bc-8ce2-b9fb177d87b6 |
| current-catalog-service-hash | ae870bc5e265a340912cde392f23dad3671a0a881730ffdadd82f2f57d81641b |
| request-id | C47E:1A342D:E5754:1444A7:6968E773 |
| html-safe-nonce | 46183970610e9dab25532927950c34fd11cf7b36b6c275bab60416d0417c4b0e |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJDNDdFOjFBMzQyRDpFNTc1NDoxNDQ0QTc6Njk2OEU3NzMiLCJ2aXNpdG9yX2lkIjoiMjIzOTQzMzIwMDE1NDI0MDg4MyIsInJlZ2lvbl9lZGdlIjoiaWFkIiwicmVnaW9uX3JlbmRlciI6ImlhZCJ9 |
| visitor-hmac | e6daf9fc3eddc5acff7bb4bed82d678af48e131de002c28b3fca39476915af86 |
| hovercard-subject-tag | pull_request:1739883635 |
| github-keyboard-shortcuts | repository,pull-request-list,pull-request-conversation,pull-request-files-changed,copilot |
| google-site-verification | Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I |
| octolytics-url | https://collector.github.com/github/collect |
| analytics-location | ///pull_requests/show/files |
| fb:app_id | 1401488693436528 |
| apple-itunes-app | app-id=1477376905, app-argument=https://github.com/gitpython-developers/GitPython/pull/1838/files |
| twitter:image | https://avatars.githubusercontent.com/u/1771172?s=400&v=4 |
| twitter:card | summary_large_image |
| og:image | https://avatars.githubusercontent.com/u/1771172?s=400&v=4 |
| og:image:alt | Fixes #1829 - cache invalidation
Fixes #1830 - typing
Fixes #1833 - parsing
Fixes #1836 - serialization
Much of the design in the fixes for #1830, #1833, and #1836, as well as why I think it makes ... |
| og:site_name | GitHub |
| og:type | object |
| hostname | github.com |
| expected-hostname | github.com |
| None | b5416305695900bdab7d289f90ea3d96bf36397112f2ab16f5a5a120f38de085 |
| turbo-cache-control | no-preview |
| diff-view | unified |
| go-import | github.com/gitpython-developers/GitPython git https://github.com/gitpython-developers/GitPython.git |
| octolytics-dimension-user_id | 503709 |
| octolytics-dimension-user_login | gitpython-developers |
| octolytics-dimension-repository_id | 1126087 |
| octolytics-dimension-repository_nwo | gitpython-developers/GitPython |
| octolytics-dimension-repository_public | true |
| octolytics-dimension-repository_is_fork | false |
| octolytics-dimension-repository_network_root_id | 1126087 |
| octolytics-dimension-repository_network_root_nwo | gitpython-developers/GitPython |
| turbo-body-classes | logged-out env-production page-responsive full-width |
| disable-turbo | true |
| browser-stats-url | https://api.github.com/_private/browser/stats |
| browser-errors-url | https://api.github.com/_private/browser/errors |
| release | 366035abf3d5b1e31841c97e2fa2ce02a3007a7b |
| ui-target | full |
| theme-color | #1e2327 |
| color-scheme | light dark |
Links:
| Skip to content | https://github.com/gitpython-developers/GitPython/pull/1838/files#start-of-content |
|
| https://github.com/ |
|
Sign in
| https://github.com/login?return_to=https%3A%2F%2Fgithub.com%2Fgitpython-developers%2FGitPython%2Fpull%2F1838%2Ffiles |
| GitHub CopilotWrite better code with AI | https://github.com/features/copilot |
| GitHub SparkBuild and deploy intelligent apps | https://github.com/features/spark |
| GitHub ModelsManage and compare prompts | https://github.com/features/models |
| MCP RegistryNewIntegrate external tools | https://github.com/mcp |
| ActionsAutomate any workflow | https://github.com/features/actions |
| CodespacesInstant dev environments | https://github.com/features/codespaces |
| IssuesPlan and track work | https://github.com/features/issues |
| Code ReviewManage code changes | https://github.com/features/code-review |
| GitHub Advanced SecurityFind and fix vulnerabilities | https://github.com/security/advanced-security |
| Code securitySecure your code as you build | https://github.com/security/advanced-security/code-security |
| Secret protectionStop leaks before they start | https://github.com/security/advanced-security/secret-protection |
| Why GitHub | https://github.com/why-github |
| Documentation | https://docs.github.com |
| Blog | https://github.blog |
| Changelog | https://github.blog/changelog |
| Marketplace | https://github.com/marketplace |
| View all features | https://github.com/features |
| Enterprises | https://github.com/enterprise |
| Small and medium teams | https://github.com/team |
| Startups | https://github.com/enterprise/startups |
| Nonprofits | https://github.com/solutions/industry/nonprofits |
| App Modernization | https://github.com/solutions/use-case/app-modernization |
| DevSecOps | https://github.com/solutions/use-case/devsecops |
| DevOps | https://github.com/solutions/use-case/devops |
| CI/CD | https://github.com/solutions/use-case/ci-cd |
| View all use cases | https://github.com/solutions/use-case |
| Healthcare | https://github.com/solutions/industry/healthcare |
| Financial services | https://github.com/solutions/industry/financial-services |
| Manufacturing | https://github.com/solutions/industry/manufacturing |
| Government | https://github.com/solutions/industry/government |
| View all industries | https://github.com/solutions/industry |
| View all solutions | https://github.com/solutions |
| AI | https://github.com/resources/articles?topic=ai |
| Software Development | https://github.com/resources/articles?topic=software-development |
| DevOps | https://github.com/resources/articles?topic=devops |
| Security | https://github.com/resources/articles?topic=security |
| View all topics | https://github.com/resources/articles |
| Customer stories | https://github.com/customer-stories |
| Events & webinars | https://github.com/resources/events |
| Ebooks & reports | https://github.com/resources/whitepapers |
| Business insights | https://github.com/solutions/executive-insights |
| GitHub Skills | https://skills.github.com |
| Documentation | https://docs.github.com |
| Customer support | https://support.github.com |
| Community forum | https://github.com/orgs/community/discussions |
| Trust center | https://github.com/trust-center |
| Partners | https://github.com/partners |
| GitHub SponsorsFund open source developers | https://github.com/sponsors |
| Security Lab | https://securitylab.github.com |
| Maintainer Community | https://maintainers.github.com |
| Accelerator | https://github.com/accelerator |
| Archive Program | https://archiveprogram.github.com |
| Topics | https://github.com/topics |
| Trending | https://github.com/trending |
| Collections | https://github.com/collections |
| Enterprise platformAI-powered developer platform | https://github.com/enterprise |
| GitHub Advanced SecurityEnterprise-grade security features | https://github.com/security/advanced-security |
| Copilot for BusinessEnterprise-grade AI features | https://github.com/features/copilot/copilot-business |
| Premium SupportEnterprise-grade 24/7 support | https://github.com/premium-support |
| Pricing | https://github.com/pricing |
| Search syntax tips | https://docs.github.com/search-github/github-code-search/understanding-github-code-search-syntax |
| documentation | https://docs.github.com/search-github/github-code-search/understanding-github-code-search-syntax |
|
Sign in
| https://github.com/login?return_to=https%3A%2F%2Fgithub.com%2Fgitpython-developers%2FGitPython%2Fpull%2F1838%2Ffiles |
|
Sign up
| https://github.com/signup?ref_cta=Sign+up&ref_loc=header+logged+out&ref_page=%2F%3Cuser-name%3E%2F%3Crepo-name%3E%2Fpull_requests%2Fshow%2Ffiles&source=header-repo&source_repo=gitpython-developers%2FGitPython |
| Reload | https://github.com/gitpython-developers/GitPython/pull/1838/files |
| Reload | https://github.com/gitpython-developers/GitPython/pull/1838/files |
| Reload | https://github.com/gitpython-developers/GitPython/pull/1838/files |
|
gitpython-developers
| https://github.com/gitpython-developers |
| GitPython | https://github.com/gitpython-developers/GitPython |
| Please reload this page | https://github.com/gitpython-developers/GitPython/pull/1838/files |
|
Notifications
| https://github.com/login?return_to=%2Fgitpython-developers%2FGitPython |
|
Fork
964
| https://github.com/login?return_to=%2Fgitpython-developers%2FGitPython |
|
Star
5k
| https://github.com/login?return_to=%2Fgitpython-developers%2FGitPython |
|
Code
| https://github.com/gitpython-developers/GitPython |
|
Issues
169
| https://github.com/gitpython-developers/GitPython/issues |
|
Pull requests
8
| https://github.com/gitpython-developers/GitPython/pulls |
|
Discussions
| https://github.com/gitpython-developers/GitPython/discussions |
|
Actions
| https://github.com/gitpython-developers/GitPython/actions |
|
Security
Uh oh!
There was an error while loading. Please reload this page.
| https://github.com/gitpython-developers/GitPython/security |
| Please reload this page | https://github.com/gitpython-developers/GitPython/pull/1838/files |
|
Insights
| https://github.com/gitpython-developers/GitPython/pulse |
|
Code
| https://github.com/gitpython-developers/GitPython |
|
Issues
| https://github.com/gitpython-developers/GitPython/issues |
|
Pull requests
| https://github.com/gitpython-developers/GitPython/pulls |
|
Discussions
| https://github.com/gitpython-developers/GitPython/discussions |
|
Actions
| https://github.com/gitpython-developers/GitPython/actions |
|
Security
| https://github.com/gitpython-developers/GitPython/security |
|
Insights
| https://github.com/gitpython-developers/GitPython/pulse |
| Sign up for GitHub
| https://github.com/signup?return_to=%2Fgitpython-developers%2FGitPython%2Fissues%2Fnew%2Fchoose |
| terms of service | https://docs.github.com/terms |
| privacy statement | https://docs.github.com/privacy |
| Sign in | https://github.com/login?return_to=%2Fgitpython-developers%2FGitPython%2Fissues%2Fnew%2Fchoose |
| Byron | https://github.com/Byron |
| gitpython-developers:main | https://github.com/gitpython-developers/GitPython/tree/main |
| EliahKagan:refresh-version | https://github.com/EliahKagan/GitPython/tree/refresh-version |
|
Conversation
1
| https://github.com/gitpython-developers/GitPython/pull/1838 |
|
Commits
19
| https://github.com/gitpython-developers/GitPython/pull/1838/commits |
|
Checks
0
| https://github.com/gitpython-developers/GitPython/pull/1838/checks |
|
Files changed
| https://github.com/gitpython-developers/GitPython/pull/1838/files |
| Please reload this page | https://github.com/gitpython-developers/GitPython/pull/1838/files |
|
Fix version_info cache invalidation, typing, parsing, and serialization
| https://github.com/gitpython-developers/GitPython/pull/1838/files#top |
|
Show all changes
19 commits
| https://github.com/gitpython-developers/GitPython/pull/1838/files |
|
e40fc2c
Use TemporaryFile as a context manager
EliahKagan Feb 21, 2024
| https://github.com/gitpython-developers/GitPython/pull/1838/commits/e40fc2cd2677c8f7e7c067583df00572430442ec |
|
1de2fdc
Test that version_info caches
EliahKagan Feb 21, 2024
| https://github.com/gitpython-developers/GitPython/pull/1838/commits/1de2fdc5234e1d688386f3a345f9cf2f264ba5ea |
|
3f107d5
Test that version_info caching is per instance
EliahKagan Feb 21, 2024
| https://github.com/gitpython-developers/GitPython/pull/1838/commits/3f107d59399538594fa98d5e3956e4cfe707494b |
|
2eac36c
Have _fake_git fixture take version_info
EliahKagan Feb 21, 2024
| https://github.com/gitpython-developers/GitPython/pull/1838/commits/2eac36c951553e8554609fdad026e69e7e1e561e |
|
2d6311b
Test version_info on unpickled Git instance
EliahKagan Feb 22, 2024
| https://github.com/gitpython-developers/GitPython/pull/1838/commits/2d6311bf35ee1f1bf6a7347edc06327cfa18b118 |
|
f699a38
Fix Git.version_info pickling
EliahKagan Feb 22, 2024
| https://github.com/gitpython-developers/GitPython/pull/1838/commits/f699a387e19764e591dbca392035a5f9e457b06d |
|
634b618
Use documented version_info in test_index_file_diffing
EliahKagan Feb 22, 2024
| https://github.com/gitpython-developers/GitPython/pull/1838/commits/634b61812cd14a13cccb975d5d54d34001c56449 |
|
626a550
Test that refreshing invalidates cached version_info
EliahKagan Feb 21, 2024
| https://github.com/gitpython-developers/GitPython/pull/1838/commits/626a550d22d91dc94b7cc0bfda3fef3ab7f10189 |
|
9151c38
Test "installing" another git and refreshing
EliahKagan Feb 22, 2024
| https://github.com/gitpython-developers/GitPython/pull/1838/commits/9151c38b18474d6642857ece114a39e34fe7dd09 |
|
c2d72ff
Simplify patched PATH env var construction
EliahKagan Feb 22, 2024
| https://github.com/gitpython-developers/GitPython/pull/1838/commits/c2d72ffde8b046d1f6d5314512fad24bd1b923eb |
|
ee385bd
Make "install" and refresh version_info test portable
EliahKagan Feb 22, 2024
| https://github.com/gitpython-developers/GitPython/pull/1838/commits/ee385bd8058473f4fdd06c2ba7f7b980a45e3ada |
|
2940662
Test that version_info is not pickled
EliahKagan Feb 22, 2024
| https://github.com/gitpython-developers/GitPython/pull/1838/commits/29406626f985c93404ef283f4e5d922e5bf96978 |
|
e3e5687
Fix tests for Python <3.9 which lack Path.with_stem
EliahKagan Feb 22, 2024
| https://github.com/gitpython-developers/GitPython/pull/1838/commits/e3e568760f7ee002921de292dd98564fdedf6126 |
|
24b065e
Invalidate all cached version_info on refresh
EliahKagan Feb 22, 2024
| https://github.com/gitpython-developers/GitPython/pull/1838/commits/24b065e4920e11b10fb6230abcc137e2f49eb8f3 |
|
82b0a1e
Clarify comments; add assertion (helps mypy)
EliahKagan Feb 22, 2024
| https://github.com/gitpython-developers/GitPython/pull/1838/commits/82b0a1e0681a662d5d418277292cf73fd26cae92 |
|
eb438ee
Refactor and further clarify comments
EliahKagan Feb 22, 2024
| https://github.com/gitpython-developers/GitPython/pull/1838/commits/eb438ee63add2376e885890489cd5e7484cc8a9d |
|
dc6b90f
Fix version_info type hint + more refactoring
EliahKagan Feb 22, 2024
| https://github.com/gitpython-developers/GitPython/pull/1838/commits/dc6b90fa5a3617c490e95aeaf37fb80cfd17a215 |
|
ac20325
Test version_info parsing
EliahKagan Feb 22, 2024
| https://github.com/gitpython-developers/GitPython/pull/1838/commits/ac20325133649876749a12c2f611dbf66f5bc17c |
|
629fd87
Fix how version_info omits non-numeric fields
EliahKagan Feb 22, 2024
| https://github.com/gitpython-developers/GitPython/pull/1838/commits/629fd87f46a91baccb298bef6fc34ac60ecc5727 |
|
Clear filters
| https://github.com/gitpython-developers/GitPython/pull/1838/files |
| Please reload this page | https://github.com/gitpython-developers/GitPython/pull/1838/files |
| Please reload this page | https://github.com/gitpython-developers/GitPython/pull/1838/files |
|
cmd.py
| https://github.com/gitpython-developers/GitPython/pull/1838/files#diff-35a18a749eb4d6efad45e56e78a9554926be5526e2ba2159b44311e718450e88 |
|
test_git.py
| https://github.com/gitpython-developers/GitPython/pull/1838/files#diff-84c4b72d5c62026240fad69041a453efc37f7336a89035b69672cce0e8beaca1 |
|
test_index.py
| https://github.com/gitpython-developers/GitPython/pull/1838/files#diff-21c27b991511d3d0e3a14b4dfb56017689de2fa114c3edf11c577bd1ce744b72 |
| git/cmd.py | https://github.com/gitpython-developers/GitPython/pull/1838/files#diff-35a18a749eb4d6efad45e56e78a9554926be5526e2ba2159b44311e718450e88 |
|
View file
| https://github.com/EliahKagan/GitPython/blob/629fd87f46a91baccb298bef6fc34ac60ecc5727/git/cmd.py |
|
Open in desktop
| https://desktop.github.com |
| https://github.co/hiddenchars |
| https://github.com/gitpython-developers/GitPython/pull/1838/{{ revealButtonHref }} |
|
| https://github.com/gitpython-developers/GitPython/pull/1838/files#diff-35a18a749eb4d6efad45e56e78a9554926be5526e2ba2159b44311e718450e88 |
|
| https://github.com/gitpython-developers/GitPython/pull/1838/files#diff-35a18a749eb4d6efad45e56e78a9554926be5526e2ba2159b44311e718450e88 |
|
| https://github.com/gitpython-developers/GitPython/pull/1838/files#diff-35a18a749eb4d6efad45e56e78a9554926be5526e2ba2159b44311e718450e88 |
|
| https://github.com/gitpython-developers/GitPython/pull/1838/files#diff-35a18a749eb4d6efad45e56e78a9554926be5526e2ba2159b44311e718450e88 |
|
| https://github.com/gitpython-developers/GitPython/pull/1838/files#diff-35a18a749eb4d6efad45e56e78a9554926be5526e2ba2159b44311e718450e88 |
|
| https://github.com/gitpython-developers/GitPython/pull/1838/files#diff-35a18a749eb4d6efad45e56e78a9554926be5526e2ba2159b44311e718450e88 |
|
| https://github.com/gitpython-developers/GitPython/pull/1838/files#diff-35a18a749eb4d6efad45e56e78a9554926be5526e2ba2159b44311e718450e88 |
|
| https://github.com/gitpython-developers/GitPython/pull/1838/files#diff-35a18a749eb4d6efad45e56e78a9554926be5526e2ba2159b44311e718450e88 |
|
| https://github.com/gitpython-developers/GitPython/pull/1838/files#diff-35a18a749eb4d6efad45e56e78a9554926be5526e2ba2159b44311e718450e88 |
|
| https://github.com/gitpython-developers/GitPython/pull/1838/files#diff-35a18a749eb4d6efad45e56e78a9554926be5526e2ba2159b44311e718450e88 |
|
| https://github.com/gitpython-developers/GitPython/pull/1838/files#diff-35a18a749eb4d6efad45e56e78a9554926be5526e2ba2159b44311e718450e88 |
|
| https://github.com/gitpython-developers/GitPython/pull/1838/files#diff-35a18a749eb4d6efad45e56e78a9554926be5526e2ba2159b44311e718450e88 |
|
| https://github.com/gitpython-developers/GitPython/pull/1838/files#diff-35a18a749eb4d6efad45e56e78a9554926be5526e2ba2159b44311e718450e88 |
|
| https://github.com/gitpython-developers/GitPython/pull/1838/files#diff-35a18a749eb4d6efad45e56e78a9554926be5526e2ba2159b44311e718450e88 |
|
| https://github.com/gitpython-developers/GitPython/pull/1838/files#diff-35a18a749eb4d6efad45e56e78a9554926be5526e2ba2159b44311e718450e88 |
| Please reload this page | https://github.com/gitpython-developers/GitPython/pull/1838/files |
|
| https://github.com |
| Terms | https://docs.github.com/site-policy/github-terms/github-terms-of-service |
| Privacy | https://docs.github.com/site-policy/privacy-policies/github-privacy-statement |
| Security | https://github.com/security |
| Status | https://www.githubstatus.com/ |
| Community | https://github.community/ |
| Docs | https://docs.github.com/ |
| Contact | https://support.github.com?tags=dotcom-footer |
Viewport: width=device-width
URLs of crawlers that visited me.