@@ -1772,6 +1772,142 @@ def ensure_git_credential_cache_daemon(
17721772 elif self ._GIT_CREDENTIAL_CACHE_DAEMON_PROCESS .poll ():
17731773 self .ensure_git_credential_cache_daemon (socket , debug , True , cwd , env )
17741774
1775+ async def stash (self , path : str , stashMsg : str = "" ) -> dict :
1776+ """
1777+ Stash changes in a dirty working directory away
1778+ path: str Git path repository
1779+ stashMsg (optional): str
1780+ A message that describes the stash entry
1781+ """
1782+ cmd = ["git" , "stash" ]
1783+
1784+ if len (stashMsg ) > 0 :
1785+ cmd .extend (["save" , "-m" , stashMsg ])
1786+
1787+ env = os .environ .copy ()
1788+ # if the git command is run in a non-interactive terminal, it will not prompt for user input
1789+ env ["GIT_TERMINAL_PROMPT" ] = "0"
1790+
1791+ code , output , error = await execute (cmd , cwd = path , env = env )
1792+
1793+ # code 0: no changes to stash
1794+ if code != 0 :
1795+ return {"code" : code , "command" : " " .join (cmd ), "message" : error }
1796+ return {"code" : code , "message" : output , "command" : " " .join (cmd )}
1797+
1798+ async def stash_list (self , path : str ) -> dict :
1799+ """
1800+ Execute git stash list command
1801+ """
1802+ cmd = ["git" , "stash" , "list" ]
1803+
1804+ env = os .environ .copy ()
1805+ env ["GIT_TERMINAL_PROMPT" ] = "0"
1806+
1807+ code , output , error = await execute (cmd , cwd = path , env = env )
1808+
1809+ if code != 0 :
1810+ return {"code" : code , "command" : " " .join (cmd ), "message" : error }
1811+
1812+ return {"code" : code , "message" : output , "command" : " " .join (cmd )}
1813+
1814+ async def stash_show (self , path : str , index : int ) -> dict :
1815+ """
1816+ Execute git stash show command
1817+ """
1818+ # stash_index = "stash@{" + str(index) + "}"
1819+ stash_index = f"stash@{{{ index !s} }}"
1820+
1821+ cmd = ["git" , "stash" , "show" , "-p" , stash_index , "--name-only" ]
1822+
1823+ env = os .environ .copy ()
1824+ env ["GIT_TERMINAL_PROMPT" ] = "0"
1825+
1826+ code , output , error = await execute (cmd , cwd = path , env = env )
1827+
1828+ if code != 0 :
1829+ return {"code" : code , "command" : " " .join (cmd ), "message" : error }
1830+
1831+ return {"code" : code , "message" : output , "command" : " " .join (cmd )}
1832+
1833+ async def pop_stash (self , path : str , stash_index : Optional [int ] = None ) -> dict :
1834+ """
1835+ Execute git stash pop for a certain index of the stash list. If no index is provided, it will
1836+
1837+ path: str
1838+ Git path repository
1839+ stash_index: number
1840+ Index of the stash list is first applied to the current branch, then removed from the stash.
1841+ If the index is not provided, the most recent stash (index=0) will be removed from the stash.
1842+ """
1843+ cmd = ["git" , "stash" , "pop" ]
1844+
1845+ if stash_index :
1846+ cmd .append (str (stash_index ))
1847+
1848+ env = os .environ .copy ()
1849+ env ["GIT_TERMINAL_PROMPT" ] = "0"
1850+
1851+ code , output , error = await execute (cmd , cwd = path , env = env )
1852+
1853+ if code != 0 :
1854+ return {"code" : code , "command" : " " .join (cmd ), "message" : error }
1855+
1856+ return {"code" : code , "message" : output , "command" : " " .join (cmd )}
1857+
1858+ async def drop_stash (self , path , stash_index : Optional [int ] = None ) -> dict :
1859+ """
1860+ Execute git stash drop to delete a single stash entry.
1861+ If not stash_index is provided, delete the entire stash.
1862+
1863+ path: Git path repository
1864+ stash_index: number or None
1865+ Index of the stash list to remove from the stash.
1866+ If None, the entire stash is removed.
1867+ """
1868+ cmd = ["git" , "stash" ]
1869+ if stash_index is None :
1870+ cmd .append ("clear" )
1871+ else :
1872+ cmd .extend (["drop" , str (stash_index )])
1873+
1874+ env = os .environ .copy ()
1875+ env ["GIT_TERMINAL_PROMPT" ] = "0"
1876+
1877+ code , output , error = await execute (cmd , cwd = path , env = env )
1878+
1879+ if code != 0 :
1880+ return {"code" : code , "command" : " " .join (cmd ), "message" : error }
1881+
1882+ return {"code" : code , "message" : output , "command" : " " .join (cmd )}
1883+
1884+ async def apply_stash (self , path : str , stash_index : Optional [int ] = None ) -> dict :
1885+ """
1886+ Execute git stash apply to apply a single stash entry to the repository.
1887+ If not stash_index is provided, apply the latest stash.
1888+
1889+ path: str
1890+ Git path repository
1891+ stash_index: number
1892+ Index of the stash list is applied to the repository.
1893+ """
1894+ # Clear
1895+ cmd = ["git" , "stash" , "apply" ]
1896+
1897+ if stash_index is not None :
1898+ cmd .append ("stash@{" + str (stash_index ) + "}" )
1899+
1900+ env = os .environ .copy ()
1901+ env ["GIT_TERMINAL_PROMPT" ] = "0"
1902+
1903+ code , output , error = await execute (cmd , cwd = path , env = env )
1904+
1905+ # error:
1906+ if code != 0 :
1907+ return {"code" : code , "command" : " " .join (cmd ), "message" : error }
1908+
1909+ return {"code" : code , "message" : output , "command" : " " .join (cmd )}
1910+
17751911 @property
17761912 def excluded_paths (self ) -> List [str ]:
17771913 """Wildcard-style path patterns that do not support git commands.
0 commit comments