1111
1212from libvcs ._internal .run import ProgressCallbackProtocol , run
1313from libvcs ._internal .types import StrOrBytesPath , StrPath
14- from libvcs ._vendor .version import InvalidVersion , parse
14+ from libvcs ._vendor .version import InvalidVersion , Version , parse as parse_version
1515
1616_CMD = t .Union [StrOrBytesPath , Sequence [StrOrBytesPath ]]
1717
1818
19+ class InvalidBuildOptions (ValueError ):
20+ """Raised when a git version output is in an unexpected format.
21+
22+ >>> InvalidBuildOptions("...")
23+ Traceback (most recent call last):
24+ ...
25+ libvcs.cmd.git.InvalidBuildOptions: Unexpected git version output format: ...
26+ """
27+
28+ def __init__ (self , version : str , * args : object ) -> None :
29+ return super ().__init__ (f"Unexpected git version output format: { version } " )
30+
31+
1932@dataclasses .dataclass
2033class GitVersionInfo :
2134 """Information about the git version."""
@@ -1778,7 +1791,7 @@ def version(
17781791 # libvcs special behavior
17791792 check_returncode : bool | None = None ,
17801793 ** kwargs : t .Any ,
1781- ) -> str :
1794+ ) -> Version :
17821795 """Get git version. Wraps `git version <https://git-scm.com/docs/git-version>`_.
17831796
17841797 Parameters
@@ -1788,29 +1801,44 @@ def version(
17881801
17891802 Returns
17901803 -------
1791- str
1792- Raw version string output
1804+ Version
1805+ Parsed semantic version object from git version output
1806+
1807+ Raises
1808+ ------
1809+ InvalidVersion
1810+ If the git version output is in an unexpected format
17931811
17941812 Examples
17951813 --------
17961814 >>> git = Git(path=example_git_repo.path)
17971815
1798- >>> git.version()
1799- 'git version ...'
1816+ >>> version = git.version()
1817+ >>> isinstance(version.major, int)
1818+ True
18001819
1801- >>> git.version(build_options=True)
1802- 'git version ...'
1820+ >>> version = git.version(build_options=True)
1821+ >>> isinstance(version.major, int)
1822+ True
18031823 """
18041824 local_flags : list [str ] = []
18051825
18061826 if build_options is True :
18071827 local_flags .append ("--build-options" )
18081828
1809- return self .run (
1829+ output = self .run (
18101830 ["version" , * local_flags ],
18111831 check_returncode = check_returncode ,
18121832 )
18131833
1834+ # Extract version string and parse it
1835+ if output .startswith ("git version " ):
1836+ version_str = output .split ("\n " , 1 )[0 ].replace ("git version " , "" ).strip ()
1837+ return parse_version (version_str )
1838+
1839+ # Raise exception if output format is unexpected
1840+ raise InvalidVersion (f"Unexpected git version output format: { output } " )
1841+
18141842 def build_options (
18151843 self ,
18161844 * ,
@@ -1826,6 +1854,11 @@ def build_options(
18261854 GitVersionInfo
18271855 Dataclass containing structured information about the git version and build
18281856
1857+ Raises
1858+ ------
1859+ InvalidBuildOptions
1860+ If the git build options output is in an unexpected format
1861+
18291862 Examples
18301863 --------
18311864 >>> git = Git(path=example_git_repo.path)
@@ -1835,51 +1868,57 @@ def build_options(
18351868 >>> isinstance(version_info.version, str)
18361869 True
18371870 """
1838- output = self .version (build_options = True , check_returncode = check_returncode )
1871+ # Get raw output directly using run() instead of version()
1872+ output = self .run (
1873+ ["version" , "--build-options" ],
1874+ check_returncode = check_returncode ,
1875+ )
18391876
18401877 # Parse the output into a structured format
18411878 result = GitVersionInfo (version = "" )
18421879
18431880 # First line is always "git version X.Y.Z"
18441881 lines = output .strip ().split ("\n " )
1845- if lines and lines [0 ].startswith ("git version " ):
1846- version_str = lines [0 ].replace ("git version " , "" ).strip ()
1847- result .version = version_str
1848-
1849- # Parse semantic version components
1850- try :
1851- parsed_version = parse (version_str )
1852- result .version_info = (
1853- parsed_version .major ,
1854- parsed_version .minor ,
1855- parsed_version .micro ,
1856- )
1857- except InvalidVersion :
1858- # Fall back to string-only if can't be parsed
1859- result .version_info = None
1860-
1861- # Parse additional build info
1862- for line in lines [1 :]:
1863- line = line .strip ()
1864- if not line :
1865- continue
1866-
1867- if ":" in line :
1868- key , value = line .split (":" , 1 )
1869- key = key .strip ()
1870- value = value .strip ()
1871-
1872- if key == "cpu" :
1873- result .cpu = value
1874- elif key == "sizeof-long" :
1875- result .sizeof_long = value
1876- elif key == "sizeof-size_t" :
1877- result .sizeof_size_t = value
1878- elif key == "shell-path" :
1879- result .shell_path = value
1880- # Special handling for the commit line which often has no colon
1881- elif "commit" in line :
1882- result .commit = line
1882+ if not lines or not lines [0 ].startswith ("git version " ):
1883+ raise InvalidBuildOptions (f"Unexpected git version output format: { output } " )
1884+
1885+ version_str = lines [0 ].replace ("git version " , "" ).strip ()
1886+ result .version = version_str
1887+
1888+ # Parse semantic version components
1889+ try :
1890+ parsed_version = parse_version (version_str )
1891+ result .version_info = (
1892+ parsed_version .major ,
1893+ parsed_version .minor ,
1894+ parsed_version .micro ,
1895+ )
1896+ except InvalidVersion :
1897+ # Fall back to string-only if can't be parsed
1898+ result .version_info = None
1899+
1900+ # Parse additional build info
1901+ for line in lines [1 :]:
1902+ line = line .strip ()
1903+ if not line :
1904+ continue
1905+
1906+ if ":" in line :
1907+ key , value = line .split (":" , 1 )
1908+ key = key .strip ()
1909+ value = value .strip ()
1910+
1911+ if key == "cpu" :
1912+ result .cpu = value
1913+ elif key == "sizeof-long" :
1914+ result .sizeof_long = value
1915+ elif key == "sizeof-size_t" :
1916+ result .sizeof_size_t = value
1917+ elif key == "shell-path" :
1918+ result .shell_path = value
1919+ # Special handling for the commit line which often has no colon
1920+ elif "commit" in line and "no commit" not in line .lower ():
1921+ result .commit = line
18831922
18841923 return result
18851924
0 commit comments