╭─────────────╮ │scroll to top│ ╰─────────────╯ ╭ net.zig ──────────────────────────────────────────────────────╮ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╰───────────────────────────────────────────────────────────────╯
╭────╮ ╭ clone ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╮ │http│ ssh │ │ ╰────╯ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╯
1101 .argv = if (is_ssh) 1102 &.{ "git", "-c", ssh_config_arg, "clone", "--upload-pack", upload_pack_command, "--shallow-since=2000-01-01", remote_url, "client" } 1103 else 1104 &.{ "git", "clone", "--shallow-since=2000-01-01", remote_url, "client" }, 1105 .cwd = .{ .path = temp_path }, 1106 .stdin = .ignore, 1107 .stdout = .ignore, 1108 .stderr = .ignore, 1109 }); 1110 const term = try process.wait(io); 1111 if (term != .exited or term.exited != 0) { 1112 return error.GitCommandFailed; 1113 } 1114 } 1115 1116 // make sure shallow clone was successful 1117 { 1118 const hello_txt = try temp_dir.openFile(io, "client/hello.txt", .{}); 1119 hello_txt.close(io); 1120 } 1121 1122 // delete client and clone again with --shallow-exclude 1123 try cwd.deleteTree(io, temp_dir_name ++ "/client"); 1124 1125 { 1126 var process = try std.process.spawn(io, .{ 1127 .argv = if (is_ssh) 1128 &.{ "git", "-c", ssh_config_arg, "clone", "--upload-pack", upload_pack_command, "--shallow-exclude=v1", remote_url, "client" } 1129 else 1130 &.{ "git", "clone", "--shallow-exclude=v1", remote_url, "client" }, 1131 .cwd = .{ .path = temp_path }, 1132 .stdin = .ignore, 1133 .stdout = .ignore, 1134 .stderr = .ignore, 1135 }); 1136 const term = try process.wait(io); 1137 if (term != .exited or term.exited != 0) { 1138 return error.GitCommandFailed; 1139 } 1140 } 1141 1142 // make sure shallow clone was successful 1143 { 1144 const hello_txt = try temp_dir.openFile(io, "client/hello.txt", .{}); 1145 hello_txt.close(io); 1146 } 1147 1148 // delete client and clone again with --filter=blob:none 1149 try cwd.deleteTree(io, temp_dir_name ++ "/client"); 1150 1151 { 1152 var process = try std.process.spawn(io, .{ 1153 .argv = if (is_ssh) 1154 &.{ "git", "-c", ssh_config_arg, "clone", "--upload-pack", upload_pack_command, "--filter=blob:none", remote_url, "client" } 1155 else 1156 &.{ "git", "clone", "--filter=blob:none", remote_url, "client" }, 1157 .cwd = .{ .path = temp_path }, 1158 .stdin = .ignore, 1159 .stdout = .ignore, 1160 .stderr = .ignore, 1161 }); 1162 const term = try process.wait(io); 1163 if (term != .exited or term.exited != 0) { 1164 return error.GitCommandFailed; 1165 } 1166 } 1167 1168 // make sure partial clone was successful 1169 { 1170 const hello_txt = try temp_dir.openFile(io, "client/hello.txt", .{}); 1171 hello_txt.close(io); 1172 } 1173 1174 // delete client and clone again with --filter=tree:0 1175 try cwd.deleteTree(io, temp_dir_name ++ "/client"); 1176 1177 { 1178 var process = try std.process.spawn(io, .{ 1179 .argv = if (is_ssh) 1180 &.{ "git", "-c", ssh_config_arg, "clone", "--upload-pack", upload_pack_command, "--filter=tree:0", remote_url, "client" } 1181 else 1182 &.{ "git", "clone", "--filter=tree:0", remote_url, "client" }, 1183 .cwd = .{ .path = temp_path }, 1184 .stdin = .ignore, 1185 .stdout = .ignore, 1186 .stderr = .ignore, 1187 }); 1188 const term = try process.wait(io); 1189 if (term != .exited or term.exited != 0) { 1190 return error.GitCommandFailed; 1191 } 1192 } 1193 1194 // make sure treeless clone was successful 1195 { 1196 const goodbye_txt = try temp_dir.openFile(io, "client/goodbye.txt", .{}); 1197 goodbye_txt.close(io); 1198 } 1199 } else { 1200 const ssh_cmd_maybe: ?[]const u8 = if (is_ssh) blk: { 1201 const known_hosts_path = try std.fs.path.join(allocator, &.{ cwd_path, temp_dir_name, "known_hosts" }); 1202 defer allocator.free(known_hosts_path); 1203 1204 const priv_key_path = try std.fs.path.join(allocator, &.{ cwd_path, temp_dir_name, "key" }); 1205 defer allocator.free(priv_key_path); 1206 1207 break :blk try std.fmt.allocPrint(allocator, "ssh -o UserKnownHostsFile=\"{s}\" -o IdentityFile=\"{s}\"", .{ known_hosts_path, priv_key_path }); 1208 } else null; 1209 defer if (ssh_cmd_maybe) |ssh_cmd| allocator.free(ssh_cmd); 1210 1211 // clone repo 1212 var client_repo = try rp.Repo(repo_kind, .{ .is_test = true }).clone( 1213 io, 1214 allocator, 1215 remote_url, 1216 temp_path, 1217 client_path, 1218 .{ .wire = .{ .ssh = .{ 1219 .command = ssh_cmd_maybe, 1220 .upload_pack_command = upload_pack_command, 1221 } } }, 1222 ); 1223 defer client_repo.deinit(io, allocator); 1224 1225 // make sure HEAD points to the right default branch 1226 var current_branch_buffer = [_]u8{0} ** rf.MAX_REF_CONTENT_SIZE; 1227 const head = try client_repo.head(io, &current_branch_buffer); 1228 try std.testing.expectEqualStrings("main", head.ref.name); 1229 1230 // make sure clone was successful 1231 const hello_txt = try temp_dir.openFile(io, "client/hello.txt", .{}); 1232 defer hello_txt.close(io); 1233 } 1234 } 1235 1236 fn testFetchLarge( 1237 comptime repo_kind: rp.RepoKind, 1238 comptime transport_def: net.TransportDefinition, 1239 comptime port: u16, 1240 comptime shell_out_to_git: bool, 1241 io: std.Io, 1242 allocator: std.mem.Allocator, 1243 ) !void { 1244 const temp_dir_name = "temp-testnet-fetch-large"; 1245 1246 // create the temp dir 1247 const cwd = std.Io.Dir.cwd(); 1248 var temp_dir_or_err = cwd.openDir(io, temp_dir_name, .{}); 1249 if (temp_dir_or_err) |*temp_dir| { 1250 temp_dir.close(io); 1251 try cwd.deleteTree(io, temp_dir_name); 1252 } else |_| {} 1253 var temp_dir = try cwd.createDirPathOpen(io, temp_dir_name, .{}); 1254 defer cwd.deleteTree(io, temp_dir_name) catch {}; 1255 defer temp_dir.close(io); 1256 1257 // init server 1258 var server = try Server(transport_def, temp_dir_name, port).init(io, allocator); 1259 try server.start(); 1260 defer server.stop(); 1261 1262 const cwd_path = try std.process.currentPathAlloc(io, allocator); 1263 defer allocator.free(cwd_path); 1264 1265 const server_path = try std.fs.path.join(allocator, &.{ cwd_path, temp_dir_name, "repos", "server" }); 1266 defer allocator.free(server_path); 1267 1268 var server_repo = try rp.Repo(.xit, .{ .is_test = true }).init(io, allocator, .{ .path = server_path }); 1269 defer server_repo.deinit(io, allocator); 1270 1271 var server_dir = try cwd.openDir(io, server_path, .{}); 1272 defer server_dir.close(io); 1273 1274 // copy files from current repo into server dir 1275 for (&[_][]const u8{"src"}) |dir_name| { 1276 var src_repo_dir = try cwd.openDir(io, dir_name, .{ .iterate = true }); 1277 defer src_repo_dir.close(io); 1278 1279 var dest_repo_dir = try server_dir.createDirPathOpen(io, dir_name, .{}); 1280 defer dest_repo_dir.close(io); 1281 1282 try copyDir(io, src_repo_dir, dest_repo_dir); 1283 1284 try server_repo.add(io, allocator, &.{dir_name}); 1285 } 1286 1287 // make a commit 1288 const commit1 = try server_repo.commit(io, allocator, .{ .message = "let there be light" }); 1289 1290 // export server repo 1291 { 1292 const export_file = try server_repo.core.repo_dir.createFile(io, "git-daemon-export-ok", .{}); 1293 defer export_file.close(io); 1294 } 1295 1296 if (shell_out_to_git) { 1297 try server_repo.addConfig(io, allocator, .{ .name = "uploadpack.allowrefinwant", .value = "true" }); 1298 } 1299 1300 const client_path = try std.fs.path.join(allocator, &.{ cwd_path, temp_dir_name, "client" }); 1301 defer allocator.free(client_path); 1302 1303 var client_repo = try rp.Repo(repo_kind, .{ .is_test = true }).init(io, allocator, .{ .path = client_path }); 1304 defer client_repo.deinit(io, allocator); 1305 1306 // add remote 1307 { 1308 if (.windows == builtin.os.tag) { 1309 std.mem.replaceScalar(u8, server_path, '\\', '/'); 1310 } 1311 const separator = if (server_path[0] == '/') "" else "/"; 1312 1313 const remote_url = switch (transport_def) { 1314 //.file => try std.fmt.allocPrint(allocator, "file://{s}{s}", .{ separator, server_path }), 1315 .file => try std.fmt.allocPrint(allocator, "../server", .{}), // relative file paths work too 1316 .wire => |wire_kind| switch (wire_kind) { 1317 .http => try std.fmt.allocPrint(allocator, "http://localhost:{}/server", .{port}), 1318 .raw => try std.fmt.allocPrint(allocator, "git://localhost:{}/server", .{port}), 1319 .ssh => try std.fmt.allocPrint(allocator, "ssh://localhost:{}{s}{s}", .{ port, separator, server_path }), 1320 }, 1321 }; 1322 defer allocator.free(remote_url); 1323 1324 try client_repo.addRemote(io, allocator, .{ .name = "origin", .value = remote_url }); 1325 try client_repo.addConfig(io, allocator, .{ .name = "branch.master.remote", .value = "origin" }); 1326 } 1327 1328 const is_ssh = switch (transport_def) { 1329 .file => false, 1330 .wire => |wire_kind| .ssh == wire_kind, 1331 }; 1332 1333 const upload_pack_command = try uploadPackCommand(is_ssh, allocator, cwd_path, port); 1334 defer allocator.free(upload_pack_command); 1335 1336 if (shell_out_to_git) { 1337 const priv_key_path = try std.fs.path.join(allocator, &.{ cwd_path, temp_dir_name, "key" }); 1338 defer allocator.free(priv_key_path); 1339 const ssh_config_arg = try std.fmt.allocPrint(allocator, "core.sshCommand=ssh -o StrictHostKeyChecking=no -o IdentityFile={s}", .{priv_key_path}); 1340 defer allocator.free(ssh_config_arg); 1341 1342 { 1343 var process = try std.process.spawn(io, .{ 1344 .argv = if (is_ssh) 1345 &.{ "git", "-c", ssh_config_arg, "pull", "--upload-pack", upload_pack_command, "origin", "master" } 1346 else 1347 &.{ "git", "pull", "origin", "master" }, 1348 .cwd = .{ .path = client_path }, 1349 .stdin = .ignore, 1350 .stdout = .ignore, 1351 .stderr = .ignore, 1352 }); 1353 const term = try process.wait(io); 1354 if (term != .exited or term.exited != 0) { 1355 return error.GitCommandFailed; 1356 } 1357 } 1358 1359 // make sure pull was successful 1360 { 1361 const oid_master = (try client_repo.readRef(io, .{ .kind = .head, .name = "master" })).?; 1362 try std.testing.expectEqualStrings(&commit1, &oid_master); 1363 } 1364 1365 // make another commit on the server 1366 const commit2 = blk: { 1367 const extra_txt = try server_repo.core.work_dir.createFile(io, "extra.txt", .{ .truncate = true }); 1368 defer extra_txt.close(io); 1369 try extra_txt.writeStreamingAll(io, "extra content"); 1370 try server_repo.add(io, allocator, &.{"extra.txt"}); 1371 break :blk try server_repo.commit(io, allocator, .{ .message = "add extra file" }); 1372 }; 1373 1374 // fetch with ref-in-want (git uses want-ref in protocol v2 when fetching named refs) 1375 { 1376 var process = try std.process.spawn(io, .{ 1377 .argv = if (is_ssh) 1378 &.{ "git", "-c", ssh_config_arg, "fetch", "--upload-pack", upload_pack_command, "origin", "master" } 1379 else 1380 &.{ "git", "fetch", "origin", "master" }, 1381 .cwd = .{ .path = client_path }, 1382 .stdin = .ignore, 1383 .stdout = .ignore, 1384 .stderr = .ignore, 1385 }); 1386 const term = try process.wait(io); 1387 if (term != .exited or term.exited != 0) { 1388 return error.GitCommandFailed; 1389 } 1390 } 1391 1392 // make sure fetch with want-ref was successful 1393 { 1394 const oid_remote_master = (try client_repo.readRef(io, .{ .kind = .{ .remote = "origin" }, .name = "master" })).?; 1395 try std.testing.expectEqualStrings(&commit2, &oid_remote_master); 1396 } 1397 } else { 1398 const refspecs = &.{ 1399 "+refs/heads/master:refs/heads/master", 1400 }; 1401 1402 const ssh_cmd_maybe: ?[]const u8 = if (is_ssh) blk: { 1403 const known_hosts_path = try std.fs.path.join(allocator, &.{ cwd_path, temp_dir_name, "known_hosts" }); 1404 defer allocator.free(known_hosts_path); 1405 1406 const priv_key_path = try std.fs.path.join(allocator, &.{ cwd_path, temp_dir_name, "key" }); 1407 defer allocator.free(priv_key_path); 1408 1409 break :blk try std.fmt.allocPrint(allocator, "ssh -o UserKnownHostsFile=\"{s}\" -o IdentityFile=\"{s}\"", .{ known_hosts_path, priv_key_path }); 1410 } else null; 1411 defer if (ssh_cmd_maybe) |ssh_cmd| allocator.free(ssh_cmd); 1412 1413 try client_repo.fetch( 1414 io, 1415 allocator, 1416 "origin", 1417 .{ .refspecs = refspecs, .wire = .{ .ssh = .{ 1418 .command = ssh_cmd_maybe, 1419 .upload_pack_command = upload_pack_command, 1420 } } }, 1421 ); 1422 1423 // update the working dir 1424 try client_repo.restore(io, allocator, "."); 1425 } 1426 1427 // make sure fetch was successful 1428 { 1429 const oid_master = (try client_repo.readRef(io, .{ .kind = .head, .name = "master" })).?; 1430 try std.testing.expectEqualStrings(&commit1, &oid_master); 1431 } 1432 } 1433 1434 fn testPushLarge( 1435 comptime repo_kind: rp.RepoKind, 1436 comptime transport_def: net.TransportDefinition, 1437 comptime port: u16, 1438 comptime shell_out_to_git: bool, 1439 io: std.Io, 1440 allocator: std.mem.Allocator, 1441 ) !void { 1442 const temp_dir_name = "temp-testnet-push-large"; 1443 1444 // create the temp dir 1445 const cwd = std.Io.Dir.cwd(); 1446 var temp_dir_or_err = cwd.openDir(io, temp_dir_name, .{}); 1447 if (temp_dir_or_err) |*temp_dir| { 1448 temp_dir.close(io); 1449 try cwd.deleteTree(io, temp_dir_name); 1450 } else |_| {} 1451 var temp_dir = try cwd.createDirPathOpen(io, temp_dir_name, .{}); 1452 defer cwd.deleteTree(io, temp_dir_name) catch {}; 1453 defer temp_dir.close(io); 1454 1455 // init server 1456 var server = try Server(transport_def, temp_dir_name, port).init(io, allocator); 1457 try server.start(); 1458 defer server.stop(); 1459 1460 const cwd_path = try std.process.currentPathAlloc(io, allocator); 1461 defer allocator.free(cwd_path); 1462 1463 const server_path = try std.fs.path.join(allocator, &.{ cwd_path, temp_dir_name, "repos", "server" }); 1464 defer allocator.free(server_path); 1465 1466 var server_repo = try rp.Repo(.xit, .{ .is_test = true }).init(io, allocator, .{ .path = server_path }); 1467 defer server_repo.deinit(io, allocator); 1468 1469 // add config 1470 switch (transport_def) { 1471 .file => try server_repo.addConfig(io, allocator, .{ .name = "core.bare", .value = "true" }), 1472 .wire => { 1473 try server_repo.addConfig(io, allocator, .{ .name = "core.bare", .value = "false" }); 1474 try server_repo.addConfig(io, allocator, .{ .name = "receive.denycurrentbranch", .value = "updateinstead" }); 1475 }, 1476 } 1477 try server_repo.addConfig(io, allocator, .{ .name = "http.receivepack", .value = "true" }); 1478 1479 // export server repo 1480 { 1481 const export_file = try server_repo.core.repo_dir.createFile(io, "git-daemon-export-ok", .{}); 1482 defer export_file.close(io); 1483 } 1484 1485 const client_path = try std.fs.path.join(allocator, &.{ cwd_path, temp_dir_name, "client" }); 1486 defer allocator.free(client_path); 1487 1488 var client_repo = try rp.Repo(repo_kind, .{ .is_test = true }).init(io, allocator, .{ .path = client_path }); 1489 defer client_repo.deinit(io, allocator); 1490 1491 var client_dir = try cwd.openDir(io, client_path, .{}); 1492 defer client_dir.close(io); 1493 1494 { 1495 const hello_txt = try client_repo.core.work_dir.createFile(io, "hello.txt", .{ .truncate = true }); 1496 defer hello_txt.close(io); 1497 try hello_txt.writeStreamingAll(io, "hello, world!"); 1498 try client_repo.add(io, allocator, &.{"hello.txt"}); 1499 } 1500 1501 // copy files from current repo into client dir 1502 for (&[_][]const u8{"src"}) |dir_name| { 1503 var src_repo_dir = try cwd.openDir(io, dir_name, .{ .iterate = true }); 1504 defer src_repo_dir.close(io); 1505 1506 var dest_repo_dir = try client_dir.createDirPathOpen(io, dir_name, .{}); 1507 defer dest_repo_dir.close(io); 1508 1509 try copyDir(io, src_repo_dir, dest_repo_dir); 1510 1511 try client_repo.add(io, allocator, &.{dir_name}); 1512 } 1513 1514 _ = try client_repo.commit(io, allocator, .{ .message = "let there be light" }); 1515 1516 // change the files so git will send them as delta objects 1517 for (&[_][]const u8{"src"}) |dir_name| { 1518 var dest_repo_dir = try client_dir.createDirPathOpen(io, dir_name, .{ .open_options = .{ .iterate = true } }); 1519 defer dest_repo_dir.close(io); 1520 1521 { 1522 var iter = dest_repo_dir.iterate(); 1523 while (try iter.next(io)) |entry| { 1524 switch (entry.kind) { 1525 .file => { 1526 const file = try dest_repo_dir.openFile(io, entry.name, .{ .mode = .read_write }); 1527 defer file.close(io); 1528 var writer = file.writer(io, &.{}); 1529 try writer.interface.writeAll("EDIT"); 1530 }, 1531 else => {}, 1532 } 1533 } 1534 } 1535 1536 try client_repo.add(io, allocator, &.{dir_name}); 1537 } 1538 1539 const commit2 = try client_repo.commit(io, allocator, .{ .message = "more stuff" }); 1540 1541 // add remote 1542 { 1543 if (.windows == builtin.os.tag) { 1544 std.mem.replaceScalar(u8, server_path, '\\', '/'); 1545 } 1546 const separator = if (server_path[0] == '/') "" else "/"; 1547 1548 const remote_url = switch (transport_def) { 1549 //.file => try std.fmt.allocPrint(allocator, "file://{s}{s}", .{ separator, server_path }), 1550 .file => try std.fmt.allocPrint(allocator, "../server", .{}), // relative file paths work too 1551 .wire => |wire_kind| switch (wire_kind) { 1552 .http => try std.fmt.allocPrint(allocator, "http://localhost:{}/server", .{port}), 1553 .raw => try std.fmt.allocPrint(allocator, "git://localhost:{}/server", .{port}), 1554 .ssh => try std.fmt.allocPrint(allocator, "ssh://localhost:{}{s}{s}", .{ port, separator, server_path }), 1555 }, 1556 }; 1557 defer allocator.free(remote_url); 1558 1559 try client_repo.addRemote(io, allocator, .{ .name = "origin", .value = remote_url }); 1560 try client_repo.addConfig(io, allocator, .{ .name = "branch.master.remote", .value = "origin" }); 1561 } 1562 1563 const is_ssh = switch (transport_def) { 1564 .file => false, 1565 .wire => |wire_kind| .ssh == wire_kind, 1566 }; 1567 1568 const receive_pack_command = try receivePackCommand(is_ssh, allocator, cwd_path, port); 1569 defer allocator.free(receive_pack_command); 1570 1571 if (shell_out_to_git) { 1572 const priv_key_path = try std.fs.path.join(allocator, &.{ cwd_path, temp_dir_name, "key" }); 1573 defer allocator.free(priv_key_path); 1574 const ssh_config_arg = try std.fmt.allocPrint(allocator, "core.sshCommand=ssh -o StrictHostKeyChecking=no -o IdentityFile={s}", .{priv_key_path}); 1575 defer allocator.free(ssh_config_arg); 1576 1577 // shell out to git so it will send delta objects 1578 var process = try std.process.spawn(io, .{ 1579 .argv = if (is_ssh) 1580 &.{ "git", "-c", ssh_config_arg, "push", "--receive-pack", receive_pack_command, "origin", "master" } 1581 else 1582 &.{ "git", "push", "origin", "master" }, 1583 .cwd = .{ .path = client_path }, 1584 .stdin = .ignore, 1585 .stdout = .ignore, 1586 .stderr = .ignore, 1587 }); 1588 const term = try process.wait(io); 1589 if (term != .exited or term.exited != 0) { 1590 return error.GitCommandFailed; 1591 } 1592 } else { 1593 const ssh_cmd_maybe: ?[]const u8 = if (is_ssh) blk: { 1594 const known_hosts_path = try std.fs.path.join(allocator, &.{ cwd_path, temp_dir_name, "known_hosts" }); 1595 defer allocator.free(known_hosts_path); 1596 1597 const priv_key_path = try std.fs.path.join(allocator, &.{ cwd_path, temp_dir_name, "key" }); 1598 defer allocator.free(priv_key_path); 1599 1600 break :blk try std.fmt.allocPrint(allocator, "ssh -o UserKnownHostsFile=\"{s}\" -o IdentityFile=\"{s}\"", .{ known_hosts_path, priv_key_path }); 1601 } else null; 1602 defer if (ssh_cmd_maybe) |ssh_cmd| allocator.free(ssh_cmd); 1603 1604 try client_repo.push( 1605 io, 1606 allocator, 1607 "origin", 1608 "master", 1609 false, 1610 .{ .wire = .{ .ssh = .{ 1611 .command = ssh_cmd_maybe, 1612 .receive_pack_command = receive_pack_command, 1613 } } }, 1614 ); 1615 1616 if (transport_def == .file) { 1617 // update the working dir 1618 try server_repo.restore(io, allocator, "."); 1619 } 1620 } 1621 1622 // make sure push was successful 1623 { 1624 const oid_master = (try server_repo.readRef(io, .{ .kind = .head, .name = "master" })).?; 1625 try std.testing.expectEqualStrings(&commit2, &oid_master); 1626 1627 const hello_txt = try temp_dir.openFile(io, "repos/server/hello.txt", .{}); 1628 defer hello_txt.close(io); 1629 } 1630 } 1631 1632 fn copyDir(io: std.Io, src_dir: std.Io.Dir, dest_dir: std.Io.Dir) !void { 1633 var iter = src_dir.iterate(); 1634 while (try iter.next(io)) |entry| { 1635 switch (entry.kind) { 1636 .file => try src_dir.copyFile(entry.name, dest_dir, entry.name, io, .{}), 1637 .directory => { 1638 try dest_dir.createDirPath(io, entry.name); 1639 var dest_entry_dir = try dest_dir.openDir(io, entry.name, .{ .access_sub_paths = true, .iterate = true, .follow_symlinks = false }); 1640 defer dest_entry_dir.close(io); 1641 var src_entry_dir = try src_dir.openDir(io, entry.name, .{ .access_sub_paths = true, .iterate = true, .follow_symlinks = false }); 1642 defer src_entry_dir.close(io); 1643 try copyDir(io, src_entry_dir, dest_entry_dir); 1644 }, 1645 else => {}, 1646 } 1647 } 1648 } 1649