Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

correctly handleuname-cmd that doesn't point to an executable file#2026

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Open
gcmarx wants to merge13 commits intogitpython-developers:main
base:main
Choose a base branch
Loading
fromgcmarx:main

Conversation

gcmarx
Copy link
Contributor

I also ran into the issue from#1979. My proposed solution is thatGitPython should only try to rununame_cmd if it points to an executable file. I also wrote a short test class for theis_cygwin_git function. I don't have a machine with Cygwin, so I can't test that it actually does work, but I trust thePython docs when they say that on Cygwin,sys.platform will becygwin.

@gcmarx
Copy link
ContributorAuthor

honestly, after having written this code, I'm not sure why we're not just delegatingis_cygwin_git tosys.platform == "cygwin".

@gcmarx
Copy link
ContributorAuthor

see#2027 for a different implementation of the same basic logic

Copy link

@CopilotCopilotAI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Pull Request Overview

This PR fixes the handling of cases where the uname command (used to detect Cygwin Git) is not an executable file and adds unit tests for verifying the behavior of is_cygwin_git. It also includes an update to the AUTHORS file to add a new contributor.

  • Updated test_util.py to add tests for is_cygwin_git.
  • Revised git/util.py to check if uname_cmd is an executable file before invoking it.
  • Updated AUTHORS list.

Reviewed Changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

FileDescription
test/test_util.pyAdded unit tests for is_cygwin_git functionality
git/util.pyUpdated uname command executable check and caching logic
AUTHORSAdded new contributor

@Byron
Copy link
Member

Thanks for contributing a fix!

Indeed, I don't know why we are in the current place, but@EliahKagan probably has more information and I hope he can chime in.

Besides that, my apologies, I did click the "copilot" review button out of curiosity, please feel free to completely ignore it.

@gcmarx
Copy link
ContributorAuthor

...incidentally, is there a way for me to run the test pipeline locally without pushing my changes up first?

Copy link
Member

@EliahKaganEliahKagan left a comment
edited
Loading

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

As argued in#2027 (comment), I think we should actually mergecffa264 -- that is, this PR as it was before the change to usingstrings -- before proceeding with further changes. I think that could be done at any time. I suggest rewinding this PR to that point (you can preserve the extra commits on their own separate branch of course).

There are some minor refinements that could be included with that, but they can just as easily be done afterwards (for example, I can make a small PR later that includes them), so they're not blocking. I've included review comments below showing them as diffs.

In addition to the reasons given in#2027 (comment) for starting that way, I also think it matches the scope you may have intended for this PR originally, based on the PR title.

This is not to say that the attempt to examine the contents of thegit executable file to discern if it is a Cygwin build should be discarded entirely -- only that I recommend integrating the working changes from before that, and then continuing with that subsequently. To that end, I've also included some review comments pertaining to those changes. To avoid confusion, I have refrained from including any diffs in those.


...incidentally, is there a way for me to run the test pipeline locally without pushing my changes up first?

Yes, certainly. There are instructions in the readme, there istox.ini if you want to use thetox runner, and the commands in the CI workflows in.github/workflows can sometimes be useful. I'm also happy to help with any questions about setting that up or problems that arise with it (and please feel free to ping me about it). There is also software to attempt to run GitHub Actions workflows locally, such asact, though I have not tried that on this repository and I don't know how well it would work.

However, in order to run the Cygwin tests on your local machine--using any of those techniques or others--I think you would need to have a Windows system (or other system capable of running Windows programs) with Cygwin installed on it.

@@ -484,7 +493,8 @@ def is_cygwin_git(git_executable: PathLike) -> bool: ...


def is_cygwin_git(git_executable: Union[None, PathLike]) -> bool:
if sys.platform == "win32": # TODO: See if we can use `sys.platform != "cygwin"`.
_logger.debug(f"sys.platform = {sys.platform}, git_executable = {git_executable}")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

The commit history indicates that the Python 3.8+ syntax{sys.platform=} was attempted and it was changed to this for compatibility. So I recommend:

Suggested change
_logger.debug(f"sys.platform ={sys.platform}, git_executable ={git_executable}")
_logger.debug(f"sys.platform={sys.platform!r}, git_executable={git_executable!r}")

That's equivalent to the meaning of the original attempt, because the{var=} interpolation syntax does not add spaces around= unless written literally, and it implicitly callsrepr on the right-hand side, as!r does.

class TestIsCygwinGit:
"""Tests for :func:`is_cygwin_git`"""

def test_on_path_executable(self):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

This may help clarify what's going with the test looking like#2027 even though the implementation does not, though the wording wouldn't have to be exactly this, and the references to#533 and#1455 could be removed if desired:

Suggested change
deftest_on_path_executable(self):
deftest_on_path_executable(self):
# Currently we assume tests run on Cygwin use Cygwin git. See #533 and #1455 for background.

(See also "The semantics ofis_cygwin_git changed a few years ago" in#2027 (comment).)

Comment on lines -460 to +461
git_dir = osp.dirname(git_executable)
git_cmd = pathlib.Path(git_executable)
git_dir = git_cmd.parent
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

git_executable is most commonly just"git" here. Callingos.path.dirname on that gave the empty string, which is falsy. That would cause the suite underif not git_dir: to run, performing an explicitPATH search forgit via thepy_where helper.

But under this change, whengit_executable is"git", callingpathlib.Path on it givesPosixPath('git'). Theparent attribute of that isPosixPath('.'). This is a truthy value, so the suite underif not git_dir: does not run. I don't think that's intentional.

is_cygwin = "CYGWIN" in uname_out
git_dir = pathlib.Path(res[0]).parent if res else ""

# If it's a cygwin git, it'll have cygwin in the output of `strings git`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I think this comment is meant to go on thesubprocess.Popen call.

git_dir = pathlib.Path(res[0]).parent if res else ""

# If it's a cygwin git, it'll have cygwin in the output of `strings git`
strings_cmd = pathlib.Path(git_dir, "strings")
Copy link
Member

@EliahKaganEliahKaganMay 28, 2025
edited
Loading

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Ifstrings is to be used here, then unlikeuname, its significance is not related to where it is located relative to thegit executable. Withuname, the idea is that if it's in the samebin directory asgit then hopefully they are the same kind of program. Withstrings, the goal is just to use a workingstrings command. That can be run asstrings. But it shouldn't be found relative to thegit executable.

(Or, since this is thought to be a Cygwin environment, it could be run as/usr/bin/strings, though I think that's less reliable and not necessary. In native Windows, implicitPATH searches can be dangerous because they usually include the current directory, leading toCWE-426 vulnerabilities likeCVE-2023-40590 andCVE-2024-22190. But that doesn't affect Cygwin builds of Python--at least not when they are directly invoking the subprocesses.)

In the common case thatgit_executable was"git", and with the other code as it currently stands, this is actually just assigning"strings" tostrings_cmd. That happens because, as noted in an above comment,git_dir starts out asPosixPath('.'), which it remains rather than being resolved to the actual directory wheregit is located. Different ways of joining a path after"." have different effects, but the waypathlib.Path does it is to discard the. component, so this just returns"strings".

Comment on lines +470 to +473
if not (pathlib.Path(strings_cmd).is_file() and os.access(strings_cmd, os.X_OK)):
_logger.debug(f"Failed checking if running in CYGWIN: {strings_cmd} is not an executable")
_is_cygwin_cache[git_executable] = False
return is_cygwin
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

If"strings" were just used literally, that would not facilitate this check, because we wouldn't know wherestrings is to check it. But that's okay, because usingstrings literally also makes it unnecessary to do this check. Either the system has astrings command available for general use, in which case attempting to run it with the simple namestrings will work, or it doesn't, in which case it will fail.

Comment on lines +475 to +477
process = subprocess.Popen([strings_cmd, git_cmd], stdout=subprocess.PIPE, text=True)
strings_output, _ = process.communicate()
is_cygwin = any(x for x in strings_output if "cygwin" in x.lower())
Copy link
Member

@EliahKaganEliahKaganMay 28, 2025
edited
Loading

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

git_cmd ispathlib.Path(git_executable), so in the common case thatgit_executable is"git", the value ofgit_cmd is justPosixPath('git'). (This is independent of whether the suite underif not git_dir: runs, since nothing in it referencesgit_cmd.) When passed tostrings, that's justgit in the current directory.

Usually, that would fail because there would be no such entry. When run from the top-level directory of the GitPython repository--as in the test suite--it is thegit subdirectory in the source code. This printsstrings: Warning: 'git' is a directory on standard error and does not read any strings.

Ifgit were resolved properly first, then this would work. However, I suspect there may be a better alternative tostrings here.

Cygwin programs link tocygwin1.dll

There's no guarantee that non-Cygwingit executables don't have a mention of Cygwin somewhere in the text they carry, so I think usingstrings has a small risk of false positives in general. In addition, it will treat MSYS2git as Cygwingit, as described in the "But another reason to start slow is MSYS2" section of#2027 (comment).

A more reliable indicator that an executable targets Cygwin is that it links tocygwin1.dll. Interactively, with executables one trusts, this is natural to check withldd. The Cygwingit links tocygwin1.dll:

(.venv) ✔ ~/repos-cygwin/GitPython [gcmarx-main|…1⚑ 1]23:47 $ ldd /usr/bin/git        ntdll.dll => /cygdrive/c/Windows/SYSTEM32/ntdll.dll (0x7ff99a1b0000)        KERNEL32.DLL => /cygdrive/c/Windows/System32/KERNEL32.DLL (0x7ff999cd0000)        KERNELBASE.dll => /cygdrive/c/Windows/System32/KERNELBASE.dll (0x7ff9979b0000)        cygpcre2-8-0.dll => /usr/bin/cygpcre2-8-0.dll (0x3ff260000)        cygintl-8.dll => /usr/bin/cygintl-8.dll (0x3ff640000)        cygiconv-2.dll => /usr/bin/cygiconv-2.dll (0x3ff670000)        cygwin1.dll => /usr/bin/cygwin1.dll (0x7ff93d550000)        cygz.dll => /usr/bin/cygz.dll (0x3fedc0000)

While the MSYS2git does not:

(.venv) ✔ ~/repos-cygwin/GitPython [gcmarx-main|…1⚑ 1]23:56 $ ldd /cygdrive/c/msys64/usr/bin/git        ntdll.dll => /cygdrive/c/Windows/SYSTEM32/ntdll.dll (0x7ff99a1b0000)        KERNEL32.DLL => /cygdrive/c/Windows/System32/KERNEL32.DLL (0x7ff999cd0000)        KERNELBASE.dll => /cygdrive/c/Windows/System32/KERNELBASE.dll (0x7ff9979b0000)        msys-iconv-2.dll => /cygdrive/c/msys64/usr/bin/msys-iconv-2.dll (0x5603f0000)        msys-intl-8.dll => /cygdrive/c/msys64/usr/bin/msys-intl-8.dll (0x430b30000)        msys-2.0.dll => /cygdrive/c/msys64/usr/bin/msys-2.0.dll (0x180040000)        msys-pcre2-8-0.dll => /cygdrive/c/msys64/usr/bin/msys-pcre2-8-0.dll (0x5d1c20000)        msys-z.dll => /cygdrive/c/msys64/usr/bin/msys-z.dll (0x522fe0000)

And the Git for Windowsgit does not:

(.venv) ✔ ~/repos-cygwin/GitPython [gcmarx-main|…1⚑ 1]23:54 $ ldd /cygdrive/c/Users/ek/scoop/apps/git/current/mingw64/bin/git        ntdll.dll => /cygdrive/c/Windows/SYSTEM32/ntdll.dll (0x7ff99a1b0000)        KERNEL32.DLL => /cygdrive/c/Windows/System32/KERNEL32.DLL (0x7ff999cd0000)        KERNELBASE.dll => /cygdrive/c/Windows/System32/KERNELBASE.dll (0x7ff9979b0000)        ADVAPI32.dll => /cygdrive/c/Windows/System32/ADVAPI32.dll (0x7ff9998a0000)        msvcrt.dll => /cygdrive/c/Windows/System32/msvcrt.dll (0x7ff998f60000)        sechost.dll => /cygdrive/c/Windows/System32/sechost.dll (0x7ff998210000)        RPCRT4.dll => /cygdrive/c/Windows/System32/RPCRT4.dll (0x7ff999a80000)        bcrypt.dll => /cygdrive/c/Windows/System32/bcrypt.dll (0x7ff997ff0000)        USER32.dll => /cygdrive/c/Windows/System32/USER32.dll (0x7ff998360000)        win32u.dll => /cygdrive/c/Windows/System32/win32u.dll (0x7ff997ea0000)        libiconv-2.dll => /cygdrive/c/Users/ek/scoop/apps/git/2.49.0/mingw64/bin/libiconv-2.dll (0x7ff94eeb0000)        GDI32.dll => /cygdrive/c/Windows/System32/GDI32.dll (0x7ff9981e0000)        libpcre2-8-0.dll => /cygdrive/c/Users/ek/scoop/apps/git/2.49.0/mingw64/bin/libpcre2-8-0.dll (0x7ff9592e0000)        gdi32full.dll => /cygdrive/c/Windows/System32/gdi32full.dll (0x7ff997ed0000)        libintl-8.dll => /cygdrive/c/Users/ek/scoop/apps/git/2.49.0/mingw64/bin/libintl-8.dll (0x7ff97d720000)        libwinpthread-1.dll => /cygdrive/c/Users/ek/scoop/apps/git/2.49.0/mingw64/bin/libwinpthread-1.dll (0x7ff97dfa0000)        msvcp_win.dll => /cygdrive/c/Windows/System32/msvcp_win.dll (0x7ff997e00000)        ucrtbase.dll => /cygdrive/c/Windows/System32/ucrtbase.dll (0x7ff997d00000)        WS2_32.dll => /cygdrive/c/Windows/System32/WS2_32.dll (0x7ff999950000)        zlib1.dll => /cygdrive/c/Users/ek/scoop/apps/git/2.49.0/mingw64/bin/zlib1.dll (0x7ff97a000000)        CRYPTBASE.DLL => /cygdrive/c/Windows/SYSTEM32/CRYPTBASE.DLL (0x7ff996ac0000)

This does not require thatldd be the Cygwinldd. For example, if we are in an MSYS2 environment, itsldd would also work. (If we are in the portable MSYS2 environment that ships with Git for Windows, that is likewise the MSYS2ldd command, and it works too.) Theseldd commands seem to work fine with all kinds of dynamically linked Windows executables. I don't think statically linkingcygwin1.dll is supported, but even if it were, Cygwingit doesn't do that.

It is dangerous to runldd on an untrusted executable. However, so long as we have resolvedgit correctly, it will be the samegit command we are running to perform Git operations, so we do trust it. So it might be okay to actually useldd for this, in a manner similar tostrings (albeit with different parsing).

However, I think it might also be feasible to examine the executable more directly. Surely there are Python libraries that help with this, but one of the long-standing benefits of GitPython is its extremely minimal default dependencies. I'm unsure how complicated it would be to reliably determine this by examining the executable using only facilities available in the Python standard library. So maybeldd is the way to go after all.

I suspect there may also be altogether different strategies I have not thought of.

Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment
Reviewers

@ByronByronByron left review comments

Copilot code reviewCopilotCopilot left review comments

@EliahKaganEliahKaganEliahKagan requested changes

Assignees
No one assigned
Labels
None yet
Milestone
No milestone
Development

Successfully merging this pull request may close these issues.

3 participants
@gcmarx@Byron@EliahKagan

[8]ページ先頭

©2009-2025 Movatter.jp