@@ -879,3 +879,221 @@ def test_tag_verify_unsigned(git_repo: GitSync) -> None:
879879
880880 # verify on unsigned tag should return error message
881881 assert "error" in result .lower () or "cannot verify" in result .lower ()
882+
883+
884+ # =============================================================================
885+ # GitStashManager / GitStashEntryCmd Tests
886+ # =============================================================================
887+
888+
889+ def test_stash_push_and_list (git_repo : GitSync ) -> None :
890+ """Test GitStashManager.push() and ls()."""
891+ # Create a file and modify it to have something to stash
892+ test_file = git_repo .path / "stash_test.txt"
893+ test_file .write_text ("initial content" )
894+ git_repo .cmd .run (["add" , "stash_test.txt" ])
895+ git_repo .cmd .run (["commit" , "-m" , "Add test file" ])
896+
897+ # Modify the file
898+ test_file .write_text ("modified content" )
899+
900+ # Push to stash
901+ result = git_repo .cmd .stashes .push (message = "Test stash" )
902+
903+ # Should succeed (not "No local changes")
904+ assert "no local changes" not in result .lower ()
905+
906+ # List stashes
907+ stashes = git_repo .cmd .stashes .ls ()
908+ assert len (stashes ) >= 1
909+ assert stashes [0 ].index == 0
910+
911+
912+ def test_stash_entry_show (git_repo : GitSync ) -> None :
913+ """Test GitStashEntryCmd.show()."""
914+ # Create a stash first
915+ test_file = git_repo .path / "show_test.txt"
916+ test_file .write_text ("initial content" )
917+ git_repo .cmd .run (["add" , "show_test.txt" ])
918+ git_repo .cmd .run (["commit" , "-m" , "Add test file" ])
919+
920+ test_file .write_text ("modified for stash" )
921+ git_repo .cmd .stashes .push (message = "Show test stash" )
922+
923+ # Get stash and show
924+ stash = git_repo .cmd .stashes .get (index = 0 )
925+ assert stash is not None
926+
927+ result = stash .show ()
928+
929+ # show should display diff info
930+ assert "show_test.txt" in result or len (result ) > 0
931+
932+
933+ class StashApplyPopFixture (t .NamedTuple ):
934+ """Test fixture for GitStashEntryCmd apply/pop operations."""
935+
936+ test_id : str
937+ method : str # "apply" or "pop"
938+ removes_stash : bool
939+
940+
941+ STASH_APPLY_POP_FIXTURES : list [StashApplyPopFixture ] = [
942+ StashApplyPopFixture (
943+ test_id = "apply-stash" ,
944+ method = "apply" ,
945+ removes_stash = False ,
946+ ),
947+ StashApplyPopFixture (
948+ test_id = "pop-stash" ,
949+ method = "pop" ,
950+ removes_stash = True ,
951+ ),
952+ ]
953+
954+
955+ @pytest .mark .parametrize (
956+ list (StashApplyPopFixture ._fields ),
957+ STASH_APPLY_POP_FIXTURES ,
958+ ids = [test .test_id for test in STASH_APPLY_POP_FIXTURES ],
959+ )
960+ def test_stash_apply_pop (
961+ git_repo : GitSync ,
962+ test_id : str ,
963+ method : str ,
964+ removes_stash : bool ,
965+ ) -> None :
966+ """Test GitStashEntryCmd.apply() and pop()."""
967+ # Clear any existing stashes
968+ git_repo .cmd .stashes .clear ()
969+
970+ # Create a stash first
971+ test_file = git_repo .path / f"{ test_id } _test.txt"
972+ test_file .write_text ("initial content" )
973+ git_repo .cmd .run (["add" , f"{ test_id } _test.txt" ])
974+ git_repo .cmd .run (["commit" , "-m" , "Add test file" ])
975+
976+ test_file .write_text ("modified for stash" )
977+ git_repo .cmd .stashes .push (message = f"Test { method } " )
978+
979+ # Get stash
980+ stash = git_repo .cmd .stashes .get (index = 0 )
981+ assert stash is not None
982+
983+ # Apply or pop
984+ if method == "apply" :
985+ result = stash .apply ()
986+ else :
987+ result = stash .pop ()
988+
989+ # Should succeed
990+ assert "error" not in result .lower () or "conflict" in result .lower ()
991+
992+ # Check if stash was removed
993+ stashes_after = git_repo .cmd .stashes .ls ()
994+ if removes_stash :
995+ assert len (stashes_after ) == 0
996+ else :
997+ assert len (stashes_after ) >= 1
998+
999+
1000+ def test_stash_drop (git_repo : GitSync ) -> None :
1001+ """Test GitStashEntryCmd.drop()."""
1002+ # Clear any existing stashes
1003+ git_repo .cmd .stashes .clear ()
1004+
1005+ # Create a stash first
1006+ test_file = git_repo .path / "drop_test.txt"
1007+ test_file .write_text ("initial content" )
1008+ git_repo .cmd .run (["add" , "drop_test.txt" ])
1009+ git_repo .cmd .run (["commit" , "-m" , "Add test file" ])
1010+
1011+ test_file .write_text ("modified for stash" )
1012+ git_repo .cmd .stashes .push (message = "Drop test stash" )
1013+
1014+ # Verify stash exists
1015+ stashes = git_repo .cmd .stashes .ls ()
1016+ assert len (stashes ) == 1
1017+
1018+ # Get stash and drop
1019+ stash = git_repo .cmd .stashes .get (index = 0 )
1020+ assert stash is not None
1021+
1022+ result = stash .drop ()
1023+ assert "dropped" in result .lower ()
1024+
1025+ # Verify stash is gone
1026+ stashes_after = git_repo .cmd .stashes .ls ()
1027+ assert len (stashes_after ) == 0
1028+
1029+
1030+ def test_stash_clear (git_repo : GitSync ) -> None :
1031+ """Test GitStashManager.clear()."""
1032+ # Create multiple stashes
1033+ test_file = git_repo .path / "clear_test.txt"
1034+ test_file .write_text ("initial content" )
1035+ git_repo .cmd .run (["add" , "clear_test.txt" ])
1036+ git_repo .cmd .run (["commit" , "-m" , "Add test file" ])
1037+
1038+ # Create first stash
1039+ test_file .write_text ("modified 1" )
1040+ git_repo .cmd .stashes .push (message = "First stash" )
1041+
1042+ # Create second stash
1043+ test_file .write_text ("modified 2" )
1044+ git_repo .cmd .stashes .push (message = "Second stash" )
1045+
1046+ # Verify stashes exist
1047+ stashes = git_repo .cmd .stashes .ls ()
1048+ assert len (stashes ) >= 2
1049+
1050+ # Clear all stashes
1051+ result = git_repo .cmd .stashes .clear ()
1052+ assert result == ""
1053+
1054+ # Verify all stashes are gone
1055+ stashes_after = git_repo .cmd .stashes .ls ()
1056+ assert len (stashes_after ) == 0
1057+
1058+
1059+ def test_stash_filter (git_repo : GitSync ) -> None :
1060+ """Test GitStashManager.filter() with QueryList filtering."""
1061+ # Create a stash on master branch
1062+ test_file = git_repo .path / "filter_test.txt"
1063+ test_file .write_text ("initial content" )
1064+ git_repo .cmd .run (["add" , "filter_test.txt" ])
1065+ git_repo .cmd .run (["commit" , "-m" , "Add test file" ])
1066+
1067+ test_file .write_text ("modified for stash" )
1068+ git_repo .cmd .stashes .push (message = "Filter test stash" )
1069+
1070+ # Filter by branch
1071+ stashes = git_repo .cmd .stashes .filter (branch = "master" )
1072+ assert len (stashes ) >= 1
1073+
1074+
1075+ def test_stash_entry_create_branch (git_repo : GitSync ) -> None :
1076+ """Test GitStashEntryCmd.create_branch()."""
1077+ # Create a stash first
1078+ test_file = git_repo .path / "branch_test.txt"
1079+ test_file .write_text ("initial content" )
1080+ git_repo .cmd .run (["add" , "branch_test.txt" ])
1081+ git_repo .cmd .run (["commit" , "-m" , "Add test file" ])
1082+
1083+ test_file .write_text ("modified for stash" )
1084+ git_repo .cmd .stashes .push (message = "Branch test stash" )
1085+
1086+ # Get stash and create branch
1087+ stash = git_repo .cmd .stashes .get (index = 0 )
1088+ assert stash is not None
1089+
1090+ result = stash .create_branch ("stash-branch" )
1091+
1092+ # Should create branch and apply stash
1093+ assert "error" not in result .lower () or "already exists" in result .lower ()
1094+
1095+ # Verify branch exists (or stash was applied)
1096+ branches = git_repo .cmd .branches .ls ()
1097+ branch_names = [b .branch_name for b in branches ]
1098+ # Either branch was created or we're on it
1099+ assert "stash-branch" in branch_names or "master" in branch_names
0 commit comments