@@ -920,9 +920,12 @@ def test_add_default_paths(self) -> None:
920
920
try :
921
921
os .chdir (self .repo .path )
922
922
self .assertEqual ({"foo" , "blah" , "adir" , ".git" }, set (os .listdir ("." )))
923
+ added , ignored = porcelain .add (self .repo .path )
924
+ # Normalize paths to use forward slashes for comparison
925
+ added_normalized = [path .replace (os .sep , "/" ) for path in added ]
923
926
self .assertEqual (
924
- ([ "foo" , os . path . join ( "adir" , "afile" )], set () ),
925
- porcelain . add ( self . repo . path ),
927
+ (added_normalized , ignored ),
928
+ ([ "foo" , "adir/afile" ], set () ),
926
929
)
927
930
finally :
928
931
os .chdir (cwd )
@@ -981,7 +984,7 @@ def test_add_ignored(self) -> None:
981
984
)
982
985
self .assertIn (b"bar" , self .repo .open_index ())
983
986
self .assertEqual ({"bar" }, set (added ))
984
- self .assertEqual ({"foo" , os . path . join ( "subdir" , "" ) }, ignored )
987
+ self .assertEqual ({"foo" , "subdir/" }, ignored )
985
988
986
989
def test_add_file_absolute_path (self ) -> None :
987
990
# Absolute paths are (not yet) supported
@@ -1044,6 +1047,199 @@ def test_add_symlink_outside_repo(self) -> None:
1044
1047
index = self .repo .open_index ()
1045
1048
self .assertIn (b"symlink_to_nowhere" , index )
1046
1049
1050
+ def test_add_repo_path (self ) -> None :
1051
+ """Test adding the repository path itself should add all untracked files."""
1052
+ # Create some untracked files
1053
+ with open (os .path .join (self .repo .path , "file1.txt" ), "w" ) as f :
1054
+ f .write ("content1" )
1055
+ with open (os .path .join (self .repo .path , "file2.txt" ), "w" ) as f :
1056
+ f .write ("content2" )
1057
+
1058
+ # Add the repository path itself
1059
+ added , ignored = porcelain .add (self .repo .path , paths = [self .repo .path ])
1060
+
1061
+ # Should add all untracked files, not stage './'
1062
+ self .assertIn ("file1.txt" , added )
1063
+ self .assertIn ("file2.txt" , added )
1064
+ self .assertNotIn ("./" , added )
1065
+
1066
+ # Verify files are actually staged
1067
+ index = self .repo .open_index ()
1068
+ self .assertIn (b"file1.txt" , index )
1069
+ self .assertIn (b"file2.txt" , index )
1070
+
1071
+ def test_add_directory_contents (self ) -> None :
1072
+ """Test adding a directory adds all files within it."""
1073
+ # Create a subdirectory with multiple files
1074
+ subdir = os .path .join (self .repo .path , "subdir" )
1075
+ os .mkdir (subdir )
1076
+ with open (os .path .join (subdir , "file1.txt" ), "w" ) as f :
1077
+ f .write ("content1" )
1078
+ with open (os .path .join (subdir , "file2.txt" ), "w" ) as f :
1079
+ f .write ("content2" )
1080
+ with open (os .path .join (subdir , "file3.txt" ), "w" ) as f :
1081
+ f .write ("content3" )
1082
+
1083
+ # Add the directory
1084
+ added , ignored = porcelain .add (self .repo .path , paths = ["subdir" ])
1085
+
1086
+ # Should add all files in the directory
1087
+ self .assertEqual (len (added ), 3 )
1088
+ # Normalize paths to use forward slashes for comparison
1089
+ added_normalized = [path .replace (os .sep , "/" ) for path in added ]
1090
+ self .assertIn ("subdir/file1.txt" , added_normalized )
1091
+ self .assertIn ("subdir/file2.txt" , added_normalized )
1092
+ self .assertIn ("subdir/file3.txt" , added_normalized )
1093
+
1094
+ # Verify files are actually staged
1095
+ index = self .repo .open_index ()
1096
+ self .assertIn (b"subdir/file1.txt" , index )
1097
+ self .assertIn (b"subdir/file2.txt" , index )
1098
+ self .assertIn (b"subdir/file3.txt" , index )
1099
+
1100
+ def test_add_nested_directories (self ) -> None :
1101
+ """Test adding a directory with nested subdirectories."""
1102
+ # Create nested directory structure
1103
+ dir1 = os .path .join (self .repo .path , "dir1" )
1104
+ dir2 = os .path .join (dir1 , "dir2" )
1105
+ dir3 = os .path .join (dir2 , "dir3" )
1106
+ os .makedirs (dir3 )
1107
+
1108
+ # Add files at each level
1109
+ with open (os .path .join (dir1 , "file1.txt" ), "w" ) as f :
1110
+ f .write ("level1" )
1111
+ with open (os .path .join (dir2 , "file2.txt" ), "w" ) as f :
1112
+ f .write ("level2" )
1113
+ with open (os .path .join (dir3 , "file3.txt" ), "w" ) as f :
1114
+ f .write ("level3" )
1115
+
1116
+ # Add the top-level directory
1117
+ added , ignored = porcelain .add (self .repo .path , paths = ["dir1" ])
1118
+
1119
+ # Should add all files recursively
1120
+ self .assertEqual (len (added ), 3 )
1121
+ # Normalize paths to use forward slashes for comparison
1122
+ added_normalized = [path .replace (os .sep , "/" ) for path in added ]
1123
+ self .assertIn ("dir1/file1.txt" , added_normalized )
1124
+ self .assertIn ("dir1/dir2/file2.txt" , added_normalized )
1125
+ self .assertIn ("dir1/dir2/dir3/file3.txt" , added_normalized )
1126
+
1127
+ # Verify files are actually staged
1128
+ index = self .repo .open_index ()
1129
+ self .assertIn (b"dir1/file1.txt" , index )
1130
+ self .assertIn (b"dir1/dir2/file2.txt" , index )
1131
+ self .assertIn (b"dir1/dir2/dir3/file3.txt" , index )
1132
+
1133
+ def test_add_directory_with_tracked_files (self ) -> None :
1134
+ """Test adding a directory with some files already tracked."""
1135
+ # Create a subdirectory with files
1136
+ subdir = os .path .join (self .repo .path , "mixed" )
1137
+ os .mkdir (subdir )
1138
+
1139
+ # Create and commit one file
1140
+ tracked_file = os .path .join (subdir , "tracked.txt" )
1141
+ with open (tracked_file , "w" ) as f :
1142
+ f .write ("already tracked" )
1143
+ porcelain .add (self .repo .path , paths = [tracked_file ])
1144
+ porcelain .commit (
1145
+ repo = self .repo .path ,
1146
+ message = b"Add tracked file" ,
1147
+ author = b"test <email>" ,
1148
+ committer = b"test <email>" ,
1149
+ )
1150
+
1151
+ # Add more untracked files
1152
+ with open (os .path .join (subdir , "untracked1.txt" ), "w" ) as f :
1153
+ f .write ("new file 1" )
1154
+ with open (os .path .join (subdir , "untracked2.txt" ), "w" ) as f :
1155
+ f .write ("new file 2" )
1156
+
1157
+ # Add the directory
1158
+ added , ignored = porcelain .add (self .repo .path , paths = ["mixed" ])
1159
+
1160
+ # Should only add the untracked files
1161
+ self .assertEqual (len (added ), 2 )
1162
+ # Normalize paths to use forward slashes for comparison
1163
+ added_normalized = [path .replace (os .sep , "/" ) for path in added ]
1164
+ self .assertIn ("mixed/untracked1.txt" , added_normalized )
1165
+ self .assertIn ("mixed/untracked2.txt" , added_normalized )
1166
+ self .assertNotIn ("mixed/tracked.txt" , added )
1167
+
1168
+ # Verify the index contains all files
1169
+ index = self .repo .open_index ()
1170
+ self .assertIn (b"mixed/tracked.txt" , index )
1171
+ self .assertIn (b"mixed/untracked1.txt" , index )
1172
+ self .assertIn (b"mixed/untracked2.txt" , index )
1173
+
1174
+ def test_add_directory_with_gitignore (self ) -> None :
1175
+ """Test adding a directory respects .gitignore patterns."""
1176
+ # Create .gitignore
1177
+ with open (os .path .join (self .repo .path , ".gitignore" ), "w" ) as f :
1178
+ f .write ("*.log\n *.tmp\n build/\n " )
1179
+
1180
+ # Create directory with mixed files
1181
+ testdir = os .path .join (self .repo .path , "testdir" )
1182
+ os .mkdir (testdir )
1183
+
1184
+ # Create various files
1185
+ with open (os .path .join (testdir , "important.txt" ), "w" ) as f :
1186
+ f .write ("keep this" )
1187
+ with open (os .path .join (testdir , "debug.log" ), "w" ) as f :
1188
+ f .write ("ignore this" )
1189
+ with open (os .path .join (testdir , "temp.tmp" ), "w" ) as f :
1190
+ f .write ("ignore this too" )
1191
+ with open (os .path .join (testdir , "readme.md" ), "w" ) as f :
1192
+ f .write ("keep this too" )
1193
+
1194
+ # Create a build directory that should be ignored
1195
+ builddir = os .path .join (testdir , "build" )
1196
+ os .mkdir (builddir )
1197
+ with open (os .path .join (builddir , "output.txt" ), "w" ) as f :
1198
+ f .write ("ignore entire directory" )
1199
+
1200
+ # Add the directory
1201
+ added , ignored = porcelain .add (self .repo .path , paths = ["testdir" ])
1202
+
1203
+ # Should only add non-ignored files
1204
+ # Normalize paths to use forward slashes for comparison
1205
+ added_normalized = {path .replace (os .sep , "/" ) for path in added }
1206
+ self .assertEqual (
1207
+ added_normalized , {"testdir/important.txt" , "testdir/readme.md" }
1208
+ )
1209
+
1210
+ # Check ignored files
1211
+ # Normalize paths to use forward slashes for comparison
1212
+ ignored_normalized = {path .replace (os .sep , "/" ) for path in ignored }
1213
+ self .assertIn ("testdir/debug.log" , ignored_normalized )
1214
+ self .assertIn ("testdir/temp.tmp" , ignored_normalized )
1215
+ self .assertIn ("testdir/build/" , ignored_normalized )
1216
+
1217
+ def test_add_multiple_directories (self ) -> None :
1218
+ """Test adding multiple directories in one call."""
1219
+ # Create multiple directories
1220
+ for dirname in ["dir1" , "dir2" , "dir3" ]:
1221
+ dirpath = os .path .join (self .repo .path , dirname )
1222
+ os .mkdir (dirpath )
1223
+ # Add files to each directory
1224
+ for i in range (2 ):
1225
+ with open (os .path .join (dirpath , f"file{ i } .txt" ), "w" ) as f :
1226
+ f .write (f"content { dirname } { i } " )
1227
+
1228
+ # Add all directories at once
1229
+ added , ignored = porcelain .add (self .repo .path , paths = ["dir1" , "dir2" , "dir3" ])
1230
+
1231
+ # Should add all files from all directories
1232
+ self .assertEqual (len (added ), 6 )
1233
+ # Normalize paths to use forward slashes for comparison
1234
+ added_normalized = [path .replace (os .sep , "/" ) for path in added ]
1235
+ for dirname in ["dir1" , "dir2" , "dir3" ]:
1236
+ for i in range (2 ):
1237
+ self .assertIn (f"{ dirname } /file{ i } .txt" , added_normalized )
1238
+
1239
+ # Verify all files are staged
1240
+ index = self .repo .open_index ()
1241
+ self .assertEqual (len (index ), 6 )
1242
+
1047
1243
1048
1244
class RemoveTests (PorcelainTestCase ):
1049
1245
def test_remove_file (self ) -> None :
0 commit comments