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-controllerpull_requests
route-actionfiles
fetch-noncev2:ff907d05-cb4a-c6bc-8ce2-b9fb177d87b6
current-catalog-service-hashae870bc5e265a340912cde392f23dad3671a0a881730ffdadd82f2f57d81641b
request-idC47E:1A342D:E5754:1444A7:6968E773
html-safe-nonce46183970610e9dab25532927950c34fd11cf7b36b6c275bab60416d0417c4b0e
visitor-payloadeyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJDNDdFOjFBMzQyRDpFNTc1NDoxNDQ0QTc6Njk2OEU3NzMiLCJ2aXNpdG9yX2lkIjoiMjIzOTQzMzIwMDE1NDI0MDg4MyIsInJlZ2lvbl9lZGdlIjoiaWFkIiwicmVnaW9uX3JlbmRlciI6ImlhZCJ9
visitor-hmace6daf9fc3eddc5acff7bb4bed82d678af48e131de002c28b3fca39476915af86
hovercard-subject-tagpull_request:1739883635
github-keyboard-shortcutsrepository,pull-request-list,pull-request-conversation,pull-request-files-changed,copilot
google-site-verificationApib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I
octolytics-urlhttps://collector.github.com/github/collect
analytics-location///pull_requests/show/files
fb:app_id1401488693436528
apple-itunes-appapp-id=1477376905, app-argument=https://github.com/gitpython-developers/GitPython/pull/1838/files
twitter:imagehttps://avatars.githubusercontent.com/u/1771172?s=400&v=4
twitter:cardsummary_large_image
og:imagehttps://avatars.githubusercontent.com/u/1771172?s=400&v=4
og:image:altFixes #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_nameGitHub
og:typeobject
hostnamegithub.com
expected-hostnamegithub.com
Noneb5416305695900bdab7d289f90ea3d96bf36397112f2ab16f5a5a120f38de085
turbo-cache-controlno-preview
diff-viewunified
go-importgithub.com/gitpython-developers/GitPython git https://github.com/gitpython-developers/GitPython.git
octolytics-dimension-user_id503709
octolytics-dimension-user_logingitpython-developers
octolytics-dimension-repository_id1126087
octolytics-dimension-repository_nwogitpython-developers/GitPython
octolytics-dimension-repository_publictrue
octolytics-dimension-repository_is_forkfalse
octolytics-dimension-repository_network_root_id1126087
octolytics-dimension-repository_network_root_nwogitpython-developers/GitPython
turbo-body-classeslogged-out env-production page-responsive full-width
disable-turbotrue
browser-stats-urlhttps://api.github.com/_private/browser/stats
browser-errors-urlhttps://api.github.com/_private/browser/errors
release366035abf3d5b1e31841c97e2fa2ce02a3007a7b
ui-targetfull
theme-color#1e2327
color-schemelight dark

Links:

Skip to contenthttps://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 AIhttps://github.com/features/copilot
GitHub SparkBuild and deploy intelligent appshttps://github.com/features/spark
GitHub ModelsManage and compare promptshttps://github.com/features/models
MCP RegistryNewIntegrate external toolshttps://github.com/mcp
ActionsAutomate any workflowhttps://github.com/features/actions
CodespacesInstant dev environmentshttps://github.com/features/codespaces
IssuesPlan and track workhttps://github.com/features/issues
Code ReviewManage code changeshttps://github.com/features/code-review
GitHub Advanced SecurityFind and fix vulnerabilitieshttps://github.com/security/advanced-security
Code securitySecure your code as you buildhttps://github.com/security/advanced-security/code-security
Secret protectionStop leaks before they starthttps://github.com/security/advanced-security/secret-protection
Why GitHubhttps://github.com/why-github
Documentationhttps://docs.github.com
Bloghttps://github.blog
Changeloghttps://github.blog/changelog
Marketplacehttps://github.com/marketplace
View all featureshttps://github.com/features
Enterpriseshttps://github.com/enterprise
Small and medium teamshttps://github.com/team
Startupshttps://github.com/enterprise/startups
Nonprofitshttps://github.com/solutions/industry/nonprofits
App Modernizationhttps://github.com/solutions/use-case/app-modernization
DevSecOpshttps://github.com/solutions/use-case/devsecops
DevOpshttps://github.com/solutions/use-case/devops
CI/CDhttps://github.com/solutions/use-case/ci-cd
View all use caseshttps://github.com/solutions/use-case
Healthcarehttps://github.com/solutions/industry/healthcare
Financial serviceshttps://github.com/solutions/industry/financial-services
Manufacturinghttps://github.com/solutions/industry/manufacturing
Governmenthttps://github.com/solutions/industry/government
View all industrieshttps://github.com/solutions/industry
View all solutionshttps://github.com/solutions
AIhttps://github.com/resources/articles?topic=ai
Software Developmenthttps://github.com/resources/articles?topic=software-development
DevOpshttps://github.com/resources/articles?topic=devops
Securityhttps://github.com/resources/articles?topic=security
View all topicshttps://github.com/resources/articles
Customer storieshttps://github.com/customer-stories
Events & webinarshttps://github.com/resources/events
Ebooks & reportshttps://github.com/resources/whitepapers
Business insightshttps://github.com/solutions/executive-insights
GitHub Skillshttps://skills.github.com
Documentationhttps://docs.github.com
Customer supporthttps://support.github.com
Community forumhttps://github.com/orgs/community/discussions
Trust centerhttps://github.com/trust-center
Partnershttps://github.com/partners
GitHub SponsorsFund open source developershttps://github.com/sponsors
Security Labhttps://securitylab.github.com
Maintainer Communityhttps://maintainers.github.com
Acceleratorhttps://github.com/accelerator
Archive Programhttps://archiveprogram.github.com
Topicshttps://github.com/topics
Trendinghttps://github.com/trending
Collectionshttps://github.com/collections
Enterprise platformAI-powered developer platformhttps://github.com/enterprise
GitHub Advanced SecurityEnterprise-grade security featureshttps://github.com/security/advanced-security
Copilot for BusinessEnterprise-grade AI featureshttps://github.com/features/copilot/copilot-business
Premium SupportEnterprise-grade 24/7 supporthttps://github.com/premium-support
Pricinghttps://github.com/pricing
Search syntax tipshttps://docs.github.com/search-github/github-code-search/understanding-github-code-search-syntax
documentationhttps://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
Reloadhttps://github.com/gitpython-developers/GitPython/pull/1838/files
Reloadhttps://github.com/gitpython-developers/GitPython/pull/1838/files
Reloadhttps://github.com/gitpython-developers/GitPython/pull/1838/files
gitpython-developers https://github.com/gitpython-developers
GitPythonhttps://github.com/gitpython-developers/GitPython
Please reload this pagehttps://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 pagehttps://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 servicehttps://docs.github.com/terms
privacy statementhttps://docs.github.com/privacy
Sign inhttps://github.com/login?return_to=%2Fgitpython-developers%2FGitPython%2Fissues%2Fnew%2Fchoose
Byronhttps://github.com/Byron
gitpython-developers:mainhttps://github.com/gitpython-developers/GitPython/tree/main
EliahKagan:refresh-versionhttps://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 pagehttps://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 pagehttps://github.com/gitpython-developers/GitPython/pull/1838/files
Please reload this pagehttps://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.pyhttps://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 pagehttps://github.com/gitpython-developers/GitPython/pull/1838/files
https://github.com
Termshttps://docs.github.com/site-policy/github-terms/github-terms-of-service
Privacyhttps://docs.github.com/site-policy/privacy-policies/github-privacy-statement
Securityhttps://github.com/security
Statushttps://www.githubstatus.com/
Communityhttps://github.community/
Docshttps://docs.github.com/
Contacthttps://support.github.com?tags=dotcom-footer

Viewport: width=device-width


URLs of crawlers that visited me.