From b67305d6d70e7e6305d27131ec4524546e5f4502 Mon Sep 17 00:00:00 2001 From: --global Date: Mon, 17 Apr 2023 15:45:50 -0400 Subject: [PATCH 01/10] Revert "Revert "fix(turborepo): SCM tests and renaming (#4462)" (#4604)" This reverts commit ff8e07c54f8860758772dcd57f422bd3094dd8f6. --- cli/internal/ffi/ffi.go | 14 +- cli/internal/ffi/proto/messages.pb.go | 176 ++++++------ cli/internal/scm/git_go.go | 3 + cli/internal/scm/git_rust.go | 28 ++ crates/turborepo-ffi/messages.proto | 8 +- crates/turborepo-ffi/src/lib.rs | 10 +- crates/turborepo-scm/src/git.rs | 382 +++++++++++++++++++++----- 7 files changed, 449 insertions(+), 172 deletions(-) create mode 100644 cli/internal/scm/git_rust.go diff --git a/cli/internal/ffi/ffi.go b/cli/internal/ffi/ffi.go index 499e2c04ee846..7ac15e45ff59e 100644 --- a/cli/internal/ffi/ffi.go +++ b/cli/internal/ffi/ffi.go @@ -116,15 +116,15 @@ func stringToRef(s string) *string { } // ChangedFiles returns the files changed in between two commits, the workdir and the index, and optionally untracked files -func ChangedFiles(repoRoot string, monorepoRoot string, fromCommit string, toCommit string) ([]string, error) { +func ChangedFiles(gitRoot string, turboRoot string, fromCommit string, toCommit string) ([]string, error) { fromCommitRef := stringToRef(fromCommit) toCommitRef := stringToRef(toCommit) req := ffi_proto.ChangedFilesReq{ - RepoRoot: repoRoot, - FromCommit: fromCommitRef, - ToCommit: toCommitRef, - MonorepoRoot: monorepoRoot, + GitRoot: gitRoot, + FromCommit: fromCommitRef, + ToCommit: toCommitRef, + TurboRoot: turboRoot, } reqBuf := Marshal(&req) @@ -144,9 +144,9 @@ func ChangedFiles(repoRoot string, monorepoRoot string, fromCommit string, toCom } // PreviousContent returns the content of a file at a previous commit -func PreviousContent(repoRoot, fromCommit, filePath string) ([]byte, error) { +func PreviousContent(gitRoot, fromCommit, filePath string) ([]byte, error) { req := ffi_proto.PreviousContentReq{ - RepoRoot: repoRoot, + GitRoot: gitRoot, FromCommit: fromCommit, FilePath: filePath, } diff --git a/cli/internal/ffi/proto/messages.pb.go b/cli/internal/ffi/proto/messages.pb.go index 2f505dca23274..22992d32663a5 100644 --- a/cli/internal/ffi/proto/messages.pb.go +++ b/cli/internal/ffi/proto/messages.pb.go @@ -270,10 +270,10 @@ type ChangedFilesReq struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - RepoRoot string `protobuf:"bytes,1,opt,name=repo_root,json=repoRoot,proto3" json:"repo_root,omitempty"` - MonorepoRoot string `protobuf:"bytes,2,opt,name=monorepo_root,json=monorepoRoot,proto3" json:"monorepo_root,omitempty"` - FromCommit *string `protobuf:"bytes,3,opt,name=from_commit,json=fromCommit,proto3,oneof" json:"from_commit,omitempty"` - ToCommit *string `protobuf:"bytes,4,opt,name=to_commit,json=toCommit,proto3,oneof" json:"to_commit,omitempty"` + GitRoot string `protobuf:"bytes,1,opt,name=git_root,json=gitRoot,proto3" json:"git_root,omitempty"` + TurboRoot string `protobuf:"bytes,2,opt,name=turbo_root,json=turboRoot,proto3" json:"turbo_root,omitempty"` + FromCommit *string `protobuf:"bytes,3,opt,name=from_commit,json=fromCommit,proto3,oneof" json:"from_commit,omitempty"` + ToCommit *string `protobuf:"bytes,4,opt,name=to_commit,json=toCommit,proto3,oneof" json:"to_commit,omitempty"` } func (x *ChangedFilesReq) Reset() { @@ -308,16 +308,16 @@ func (*ChangedFilesReq) Descriptor() ([]byte, []int) { return file_turborepo_ffi_messages_proto_rawDescGZIP(), []int{4} } -func (x *ChangedFilesReq) GetRepoRoot() string { +func (x *ChangedFilesReq) GetGitRoot() string { if x != nil { - return x.RepoRoot + return x.GitRoot } return "" } -func (x *ChangedFilesReq) GetMonorepoRoot() string { +func (x *ChangedFilesReq) GetTurboRoot() string { if x != nil { - return x.MonorepoRoot + return x.TurboRoot } return "" } @@ -468,7 +468,7 @@ type PreviousContentReq struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - RepoRoot string `protobuf:"bytes,1,opt,name=repo_root,json=repoRoot,proto3" json:"repo_root,omitempty"` + GitRoot string `protobuf:"bytes,1,opt,name=git_root,json=gitRoot,proto3" json:"git_root,omitempty"` FromCommit string `protobuf:"bytes,2,opt,name=from_commit,json=fromCommit,proto3" json:"from_commit,omitempty"` FilePath string `protobuf:"bytes,3,opt,name=file_path,json=filePath,proto3" json:"file_path,omitempty"` } @@ -505,9 +505,9 @@ func (*PreviousContentReq) Descriptor() ([]byte, []int) { return file_turborepo_ffi_messages_proto_rawDescGZIP(), []int{7} } -func (x *PreviousContentReq) GetRepoRoot() string { +func (x *PreviousContentReq) GetGitRoot() string { if x != nil { - return x.RepoRoot + return x.GitRoot } return "" } @@ -1027,84 +1027,84 @@ var file_turborepo_ffi_messages_proto_rawDesc = []byte{ 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x24, 0x0a, 0x0c, 0x47, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x22, - 0xb9, 0x01, 0x0a, 0x0f, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x73, - 0x52, 0x65, 0x71, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x70, 0x6f, 0x5f, 0x72, 0x6f, 0x6f, 0x74, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x70, 0x6f, 0x52, 0x6f, 0x6f, 0x74, - 0x12, 0x23, 0x0a, 0x0d, 0x6d, 0x6f, 0x6e, 0x6f, 0x72, 0x65, 0x70, 0x6f, 0x5f, 0x72, 0x6f, 0x6f, - 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x6d, 0x6f, 0x6e, 0x6f, 0x72, 0x65, 0x70, - 0x6f, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x24, 0x0a, 0x0b, 0x66, 0x72, 0x6f, 0x6d, 0x5f, 0x63, 0x6f, - 0x6d, 0x6d, 0x69, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0a, 0x66, 0x72, - 0x6f, 0x6d, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x88, 0x01, 0x01, 0x12, 0x20, 0x0a, 0x09, 0x74, - 0x6f, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, - 0x52, 0x08, 0x74, 0x6f, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x88, 0x01, 0x01, 0x42, 0x0e, 0x0a, - 0x0c, 0x5f, 0x66, 0x72, 0x6f, 0x6d, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x42, 0x0c, 0x0a, - 0x0a, 0x5f, 0x74, 0x6f, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x22, 0x61, 0x0a, 0x10, 0x43, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x12, - 0x29, 0x0a, 0x05, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, - 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x4c, 0x69, 0x73, - 0x74, 0x48, 0x00, 0x52, 0x05, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x16, 0x0a, 0x05, 0x65, 0x72, + 0xb1, 0x01, 0x0a, 0x0f, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x73, + 0x52, 0x65, 0x71, 0x12, 0x19, 0x0a, 0x08, 0x67, 0x69, 0x74, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x67, 0x69, 0x74, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x1d, + 0x0a, 0x0a, 0x74, 0x75, 0x72, 0x62, 0x6f, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x09, 0x74, 0x75, 0x72, 0x62, 0x6f, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x24, 0x0a, + 0x0b, 0x66, 0x72, 0x6f, 0x6d, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x48, 0x00, 0x52, 0x0a, 0x66, 0x72, 0x6f, 0x6d, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, + 0x88, 0x01, 0x01, 0x12, 0x20, 0x0a, 0x09, 0x74, 0x6f, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x08, 0x74, 0x6f, 0x43, 0x6f, 0x6d, 0x6d, + 0x69, 0x74, 0x88, 0x01, 0x01, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x66, 0x72, 0x6f, 0x6d, 0x5f, 0x63, + 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x74, 0x6f, 0x5f, 0x63, 0x6f, 0x6d, + 0x6d, 0x69, 0x74, 0x22, 0x61, 0x0a, 0x10, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x46, 0x69, + 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x12, 0x29, 0x0a, 0x05, 0x66, 0x69, 0x6c, 0x65, 0x73, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, + 0x46, 0x69, 0x6c, 0x65, 0x73, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x00, 0x52, 0x05, 0x66, 0x69, 0x6c, + 0x65, 0x73, 0x12, 0x16, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x48, 0x00, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x42, 0x0a, 0x0a, 0x08, 0x72, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x28, 0x0a, 0x10, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x64, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x69, + 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x66, 0x69, 0x6c, 0x65, 0x73, + 0x22, 0x6d, 0x0a, 0x12, 0x50, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x43, 0x6f, 0x6e, 0x74, + 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x12, 0x19, 0x0a, 0x08, 0x67, 0x69, 0x74, 0x5f, 0x72, 0x6f, + 0x6f, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x67, 0x69, 0x74, 0x52, 0x6f, 0x6f, + 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x66, 0x72, 0x6f, 0x6d, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x66, 0x72, 0x6f, 0x6d, 0x43, 0x6f, 0x6d, 0x6d, + 0x69, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x50, 0x61, 0x74, 0x68, 0x22, + 0x55, 0x0a, 0x13, 0x50, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x43, 0x6f, 0x6e, 0x74, 0x65, + 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x12, 0x1a, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, + 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, + 0x6e, 0x74, 0x12, 0x16, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x48, 0x00, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x42, 0x0a, 0x0a, 0x08, 0x72, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xf0, 0x01, 0x0a, 0x15, 0x54, 0x72, 0x61, 0x6e, 0x73, + 0x69, 0x74, 0x69, 0x76, 0x65, 0x44, 0x65, 0x70, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x08, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x23, 0x0a, 0x0d, + 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x64, 0x69, 0x72, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0c, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x44, 0x69, + 0x72, 0x12, 0x53, 0x0a, 0x0f, 0x75, 0x6e, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x5f, + 0x64, 0x65, 0x70, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x54, 0x72, 0x61, + 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x44, 0x65, 0x70, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x2e, 0x55, 0x6e, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x44, 0x65, 0x70, + 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0e, 0x75, 0x6e, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, + 0x65, 0x64, 0x44, 0x65, 0x70, 0x73, 0x1a, 0x41, 0x0a, 0x13, 0x55, 0x6e, 0x72, 0x65, 0x73, 0x6f, + 0x6c, 0x76, 0x65, 0x64, 0x44, 0x65, 0x70, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, + 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, + 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x70, 0x0a, 0x16, 0x54, 0x72, 0x61, + 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x44, 0x65, 0x70, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x32, 0x0a, 0x08, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x4c, 0x6f, 0x63, 0x6b, 0x66, 0x69, 0x6c, 0x65, + 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x00, 0x52, 0x08, 0x70, + 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x12, 0x16, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x42, + 0x0a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x53, 0x0a, 0x0f, 0x4c, + 0x6f, 0x63, 0x6b, 0x66, 0x69, 0x6c, 0x65, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x12, 0x10, + 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, + 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6f, + 0x75, 0x6e, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, 0x6f, 0x75, 0x6e, 0x64, + 0x22, 0x3b, 0x0a, 0x13, 0x4c, 0x6f, 0x63, 0x6b, 0x66, 0x69, 0x6c, 0x65, 0x50, 0x61, 0x63, 0x6b, + 0x61, 0x67, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x24, 0x0a, 0x04, 0x6c, 0x69, 0x73, 0x74, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x4c, 0x6f, 0x63, 0x6b, 0x66, 0x69, 0x6c, 0x65, + 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x52, 0x04, 0x6c, 0x69, 0x73, 0x74, 0x22, 0x69, 0x0a, + 0x0f, 0x53, 0x75, 0x62, 0x67, 0x72, 0x61, 0x70, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x08, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1e, 0x0a, 0x0a, + 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, + 0x52, 0x0a, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, + 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, + 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x22, 0x54, 0x0a, 0x10, 0x53, 0x75, 0x62, 0x67, + 0x72, 0x61, 0x70, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x08, + 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, + 0x52, 0x08, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x16, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x05, 0x65, 0x72, 0x72, - 0x6f, 0x72, 0x42, 0x0a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x28, - 0x0a, 0x10, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x4c, 0x69, - 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, - 0x09, 0x52, 0x05, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x22, 0x6f, 0x0a, 0x12, 0x50, 0x72, 0x65, 0x76, - 0x69, 0x6f, 0x75, 0x73, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x12, 0x1b, - 0x0a, 0x09, 0x72, 0x65, 0x70, 0x6f, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x08, 0x72, 0x65, 0x70, 0x6f, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x66, - 0x72, 0x6f, 0x6d, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0a, 0x66, 0x72, 0x6f, 0x6d, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x12, 0x1b, 0x0a, 0x09, - 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x66, 0x69, 0x6c, 0x65, 0x50, 0x61, 0x74, 0x68, 0x22, 0x55, 0x0a, 0x13, 0x50, 0x72, 0x65, - 0x76, 0x69, 0x6f, 0x75, 0x73, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, - 0x12, 0x1a, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0c, 0x48, 0x00, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, 0x16, 0x0a, 0x05, - 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x05, 0x65, - 0x72, 0x72, 0x6f, 0x72, 0x42, 0x0a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0xf0, 0x01, 0x0a, 0x15, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x44, - 0x65, 0x70, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x6f, - 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x63, 0x6f, - 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x5f, 0x64, 0x69, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x77, - 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x44, 0x69, 0x72, 0x12, 0x53, 0x0a, 0x0f, 0x75, - 0x6e, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x5f, 0x64, 0x65, 0x70, 0x73, 0x18, 0x03, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, - 0x65, 0x44, 0x65, 0x70, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x55, 0x6e, 0x72, - 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x44, 0x65, 0x70, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x52, 0x0e, 0x75, 0x6e, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x44, 0x65, 0x70, 0x73, - 0x1a, 0x41, 0x0a, 0x13, 0x55, 0x6e, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x44, 0x65, - 0x70, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, - 0x02, 0x38, 0x01, 0x22, 0x70, 0x0a, 0x16, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, - 0x65, 0x44, 0x65, 0x70, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x32, 0x0a, - 0x08, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x14, 0x2e, 0x4c, 0x6f, 0x63, 0x6b, 0x66, 0x69, 0x6c, 0x65, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, - 0x65, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x00, 0x52, 0x08, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, - 0x73, 0x12, 0x16, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x48, 0x00, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x42, 0x0a, 0x0a, 0x08, 0x72, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x53, 0x0a, 0x0f, 0x4c, 0x6f, 0x63, 0x6b, 0x66, 0x69, 0x6c, - 0x65, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, - 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, - 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x22, 0x3b, 0x0a, 0x13, 0x4c, 0x6f, - 0x63, 0x6b, 0x66, 0x69, 0x6c, 0x65, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x4c, 0x69, 0x73, - 0x74, 0x12, 0x24, 0x0a, 0x04, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x10, 0x2e, 0x4c, 0x6f, 0x63, 0x6b, 0x66, 0x69, 0x6c, 0x65, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, - 0x65, 0x52, 0x04, 0x6c, 0x69, 0x73, 0x74, 0x22, 0x69, 0x0a, 0x0f, 0x53, 0x75, 0x62, 0x67, 0x72, - 0x61, 0x70, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x6f, - 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x63, 0x6f, - 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x77, 0x6f, 0x72, 0x6b, - 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, - 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, - 0x65, 0x73, 0x22, 0x54, 0x0a, 0x10, 0x53, 0x75, 0x62, 0x67, 0x72, 0x61, 0x70, 0x68, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x08, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, - 0x74, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x08, 0x63, 0x6f, 0x6e, 0x74, - 0x65, 0x6e, 0x74, 0x73, 0x12, 0x16, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x42, 0x0a, 0x0a, 0x08, - 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x0b, 0x5a, 0x09, 0x66, 0x66, 0x69, 0x2f, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6f, 0x72, 0x42, 0x0a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x0b, + 0x5a, 0x09, 0x66, 0x66, 0x69, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, } var ( diff --git a/cli/internal/scm/git_go.go b/cli/internal/scm/git_go.go index f3decd8a5237e..1e5060895f0bc 100644 --- a/cli/internal/scm/git_go.go +++ b/cli/internal/scm/git_go.go @@ -1,3 +1,6 @@ +//go:build go || !rust +// +build go !rust + // Package scm abstracts operations on various tools like git // Currently, only git is supported. // diff --git a/cli/internal/scm/git_rust.go b/cli/internal/scm/git_rust.go new file mode 100644 index 0000000000000..7860b6bd48cd9 --- /dev/null +++ b/cli/internal/scm/git_rust.go @@ -0,0 +1,28 @@ +// Package scm abstracts operations on various tools like git +// Currently, only git is supported. +// +// Adapted from https://github.com/thought-machine/please/tree/master/src/scm +// Copyright Thought Machine, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +//go:build rust +// +build rust + +package scm + +import ( + "github.com/vercel/turbo/cli/internal/ffi" +) + +// git implements operations on a git repository. +type git struct { + repoRoot string +} + +// ChangedFiles returns a list of modified files since the given commit, optionally including untracked files. +func (g *git) ChangedFiles(fromCommit string, toCommit string, monorepoRoot string) ([]string, error) { + return ffi.ChangedFiles(g.repoRoot, monorepoRoot, fromCommit, toCommit) +} + +func (g *git) PreviousContent(fromCommit string, filePath string) ([]byte, error) { + return ffi.PreviousContent(g.repoRoot, fromCommit, filePath) +} diff --git a/crates/turborepo-ffi/messages.proto b/crates/turborepo-ffi/messages.proto index 40d7214b225a3..8749d3b9b527d 100644 --- a/crates/turborepo-ffi/messages.proto +++ b/crates/turborepo-ffi/messages.proto @@ -24,10 +24,10 @@ message GlobRespList { } message ChangedFilesReq { - string repo_root = 1; - string monorepo_root = 2; + string git_root = 1; + string turbo_root = 2; optional string from_commit = 3; - optional string to_commit = 4; + string to_commit = 4; } message ChangedFilesResp { @@ -42,7 +42,7 @@ message ChangedFilesList { } message PreviousContentReq { - string repo_root = 1; + string git_root = 1; string from_commit = 2; string file_path = 3; } diff --git a/crates/turborepo-ffi/src/lib.rs b/crates/turborepo-ffi/src/lib.rs index 00c95e420bc92..a44fca4761693 100644 --- a/crates/turborepo-ffi/src/lib.rs +++ b/crates/turborepo-ffi/src/lib.rs @@ -74,11 +74,11 @@ pub extern "C" fn changed_files(buffer: Buffer) -> Buffer { } }; - let commit_range = req.from_commit.as_deref().zip(req.to_commit.as_deref()); let response = match turborepo_scm::git::changed_files( - req.repo_root.into(), - req.monorepo_root.into(), - commit_range, + req.git_root.into(), + req.turbo_root.into(), + req.from_commit.as_deref(), + &req.to_commit, ) { Ok(files) => { let files: Vec<_> = files.into_iter().collect(); @@ -108,7 +108,7 @@ pub extern "C" fn previous_content(buffer: Buffer) -> Buffer { }; let response = match turborepo_scm::git::previous_content( - req.repo_root.into(), + req.git_root.into(), &req.from_commit, PathBuf::from(req.file_path), ) { diff --git a/crates/turborepo-scm/src/git.rs b/crates/turborepo-scm/src/git.rs index f59a6e674d4b7..5adb8bef29ab7 100644 --- a/crates/turborepo-scm/src/git.rs +++ b/crates/turborepo-scm/src/git.rs @@ -1,4 +1,7 @@ -use std::{collections::HashSet, path::PathBuf}; +use std::{ + collections::HashSet, + path::{Path, PathBuf}, +}; use git2::{DiffFormat, DiffOptions, Repository}; use turbopath::{AbsoluteSystemPathBuf, AnchoredSystemPathBuf}; @@ -21,20 +24,36 @@ use crate::Error; pub fn changed_files( git_root: PathBuf, turbo_root: PathBuf, - commit_range: Option<(&str, &str)>, + from_commit: Option<&str>, + to_commit: &str, ) -> Result, Error> { // Initialize repository at repo root let repo = Repository::open(&git_root)?; let git_root = AbsoluteSystemPathBuf::new(git_root)?; let turbo_root = AbsoluteSystemPathBuf::new(turbo_root)?; + let anchored_turbo_root = git_root.anchor(&turbo_root)?; let mut files = HashSet::new(); - add_changed_files_from_unstaged_changes(&git_root, &repo, &turbo_root, &mut files)?; - - if let Some((from_commit, to_commit)) = commit_range { + add_changed_files_from_to_commit_to_working_tree( + &git_root, + &repo, + &anchored_turbo_root, + &turbo_root, + to_commit, + &mut files, + )?; + add_changed_files_from_unstaged_changes( + &git_root, + &repo, + &anchored_turbo_root, + &turbo_root, + &mut files, + )?; + if let Some(from_commit) = from_commit { add_changed_files_from_commits( &git_root, &repo, + &anchored_turbo_root, &turbo_root, &mut files, from_commit, @@ -45,9 +64,51 @@ pub fn changed_files( Ok(files) } +fn reanchor_path_from_git_root_to_turbo_root( + git_root: &AbsoluteSystemPathBuf, + turbo_root: &AbsoluteSystemPathBuf, + path: &Path, +) -> Result { + let anchored_to_git_root_file_path: AnchoredSystemPathBuf = path.try_into()?; + let absolute_file_path = git_root.resolve(&anchored_to_git_root_file_path); + let anchored_to_turbo_root_file_path = turbo_root.anchor(&absolute_file_path)?; + Ok(anchored_to_turbo_root_file_path) +} + +// Equivalent of `git diff --name-only -- +fn add_changed_files_from_to_commit_to_working_tree( + git_root: &AbsoluteSystemPathBuf, + repo: &Repository, + anchored_turbo_root: &AnchoredSystemPathBuf, + turbo_root: &AbsoluteSystemPathBuf, + to_commit: &str, + files: &mut HashSet, +) -> Result<(), Error> { + let to_commit_ref = repo.revparse_single(to_commit)?; + let to_commit = to_commit_ref.peel_to_commit()?; + let to_tree = to_commit.tree()?; + let mut options = DiffOptions::new(); + options.pathspec(anchored_turbo_root.to_str()?); + + let diff = repo.diff_tree_to_workdir_with_index(Some(&to_tree), Some(&mut options))?; + + for delta in diff.deltas() { + let file = delta.old_file(); + if let Some(file_path) = file.path() { + let anchored_to_turbo_root_file_path = + reanchor_path_from_git_root_to_turbo_root(git_root, turbo_root, file_path)?; + files.insert(anchored_to_turbo_root_file_path.to_str()?.to_string()); + } + } + + Ok(()) +} + +// Equivalent of `git ls-files --other --exclude-standard -- ` fn add_changed_files_from_unstaged_changes( - repo_root: &AbsoluteSystemPathBuf, + git_root: &AbsoluteSystemPathBuf, repo: &Repository, + anchored_turbo_root: &AnchoredSystemPathBuf, turbo_root: &AbsoluteSystemPathBuf, files: &mut HashSet, ) -> Result<(), Error> { @@ -55,17 +116,15 @@ fn add_changed_files_from_unstaged_changes( options.include_untracked(true); options.recurse_untracked_dirs(true); - let anchored_turbo_root = repo_root.anchor(turbo_root)?; - options.pathspec(anchored_turbo_root.to_str()?.to_string()); + options.pathspec(anchored_turbo_root.to_str()?); let diff = repo.diff_index_to_workdir(None, Some(&mut options))?; for delta in diff.deltas() { let file = delta.old_file(); if let Some(file_path) = file.path() { - let anchored_to_repo_root_file_path: AnchoredSystemPathBuf = file_path.try_into()?; - let absolute_file_path = repo_root.resolve(&anchored_to_repo_root_file_path); - let anchored_to_turbo_root_file_path = turbo_root.anchor(&absolute_file_path)?; + let anchored_to_turbo_root_file_path = + reanchor_path_from_git_root_to_turbo_root(git_root, turbo_root, file_path)?; files.insert(anchored_to_turbo_root_file_path.to_str()?.to_string()); } } @@ -73,9 +132,17 @@ fn add_changed_files_from_unstaged_changes( Ok(()) } +// Equivalent of `git diff --name-only ... -- +// ` NOTE: This is **not** the same as +// `git diff --name-only .. -- ` +// (note the triple dots vs double dots) +// The triple dot version is a diff of the most recent common ancestor (merge +// base) of and to , whereas the double dot +// version is a diff of to . fn add_changed_files_from_commits( - repo_root: &AbsoluteSystemPathBuf, + git_root: &AbsoluteSystemPathBuf, repo: &Repository, + anchored_turbo_root: &AnchoredSystemPathBuf, turbo_root: &AbsoluteSystemPathBuf, files: &mut HashSet, from_commit: &str, @@ -83,24 +150,24 @@ fn add_changed_files_from_commits( ) -> Result<(), Error> { let from_commit_ref = repo.revparse_single(from_commit)?; let to_commit_ref = repo.revparse_single(to_commit)?; - let from_commit = from_commit_ref.peel_to_commit()?; + let mergebase_of_from_and_to = repo.merge_base(from_commit_ref.id(), to_commit_ref.id())?; + + let mergebase_commit = repo.find_commit(mergebase_of_from_and_to)?; let to_commit = to_commit_ref.peel_to_commit()?; - let from_tree = from_commit.tree()?; + let mergebase_tree = mergebase_commit.tree()?; let to_tree = to_commit.tree()?; let mut options = DiffOptions::new(); - let anchored_turbo_root = repo_root.anchor(turbo_root)?; options.pathspec(anchored_turbo_root.to_str()?); - let diff = repo.diff_tree_to_tree(Some(&from_tree), Some(&to_tree), Some(&mut options))?; + let diff = repo.diff_tree_to_tree(Some(&mergebase_tree), Some(&to_tree), Some(&mut options))?; diff.print(DiffFormat::NameOnly, |_, _, _| true)?; for delta in diff.deltas() { let file = delta.old_file(); if let Some(file_path) = file.path() { - let anchored_to_repo_root_file_path: AnchoredSystemPathBuf = file_path.try_into()?; - let absolute_file_path = repo_root.resolve(&anchored_to_repo_root_file_path); - let anchored_to_turbo_root_file_path = turbo_root.anchor(&absolute_file_path)?; + let anchored_to_turbo_root_file_path = + reanchor_path_from_git_root_to_turbo_root(git_root, turbo_root, file_path)?; files.insert(anchored_to_turbo_root_file_path.to_str()?.to_string()); } } @@ -113,24 +180,24 @@ fn add_changed_files_from_commits( /// /// # Arguments /// -/// * `repo_root`: The root of the repository +/// * `git_root`: The root of the repository /// * `from_commit`: The commit hash to checkout /// * `file_path`: The path to the file /// /// returns: Result pub fn previous_content( - repo_root: PathBuf, + git_root: PathBuf, from_commit: &str, file_path: PathBuf, ) -> Result, Error> { - let repo = Repository::open(&repo_root)?; - let repo_root = AbsoluteSystemPathBuf::new(dunce::canonicalize(repo_root)?)?; + let repo = Repository::open(&git_root)?; + let git_root = AbsoluteSystemPathBuf::new(dunce::canonicalize(git_root)?)?; let file_path = AbsoluteSystemPathBuf::new(dunce::canonicalize(file_path)?)?; let from_commit_ref = repo.revparse_single(from_commit)?; let from_commit = from_commit_ref.peel_to_commit()?; let from_tree = from_commit.tree()?; - let relative_path = repo_root.anchor(&file_path)?; + let relative_path = git_root.anchor(&file_path)?; let file = from_tree.get_path(relative_path.as_path())?; let blob = repo.find_blob(file.id())?; @@ -146,12 +213,25 @@ mod tests { env::set_current_dir, fs, path::{Path, PathBuf}, + process::Command, }; use git2::{Oid, Repository}; + use tempfile::TempDir; + use turbopath::AbsoluteSystemPathBuf; use super::previous_content; - use crate::Error; + use crate::{git::reanchor_path_from_git_root_to_turbo_root, Error}; + + fn setup_repository() -> Result<(TempDir, Repository), Error> { + let repo_root = tempfile::tempdir()?; + let repo = Repository::init(repo_root.path())?; + let mut config = repo.config()?; + config.set_str("user.name", "test")?; + config.set_str("user.email", "test@example.com")?; + + Ok((repo_root, repo)) + } fn commit_file( repo: &Repository, @@ -181,14 +261,186 @@ mod tests { )?) } + fn commit_delete(repo: &Repository, path: &Path, previous_commit: Oid) -> Result { + let mut index = repo.index()?; + index.remove_path(path)?; + let tree_oid = index.write_tree()?; + index.write()?; + let tree = repo.find_tree(tree_oid)?; + let previous_commit = repo.find_commit(previous_commit)?; + + Ok(repo.commit( + Some("HEAD"), + &repo.signature()?, + &repo.signature()?, + "Commit", + &tree, + std::slice::from_ref(&&previous_commit), + )?) + } + + fn add_files_from_stdout( + files: &mut HashSet, + git_root: &AbsoluteSystemPathBuf, + turbo_root: &AbsoluteSystemPathBuf, + stdout: Vec, + ) { + let stdout = String::from_utf8(stdout).unwrap(); + for line in stdout.lines() { + let path = Path::new(line); + let anchored_to_turbo_root_file_path = + reanchor_path_from_git_root_to_turbo_root(git_root, turbo_root, path).unwrap(); + files.insert( + anchored_to_turbo_root_file_path + .to_str() + .unwrap() + .to_string(), + ); + } + } + + // An implementation that shells out to the git command like the old Go version + fn expected_changed_files( + git_root: &Path, + turbo_root: &Path, + from_commit: Option<&str>, + to_commit: &str, + ) -> Result, Error> { + let git_root = AbsoluteSystemPathBuf::new(dunce::canonicalize(git_root)?)?; + let turbo_root = AbsoluteSystemPathBuf::new(dunce::canonicalize(turbo_root)?)?; + + let mut files = HashSet::new(); + let output = Command::new("git") + .arg("diff") + .arg("--name-only") + .arg(to_commit) + .arg("--") + .arg(turbo_root.to_str().unwrap()) + .current_dir(&git_root) + .output() + .expect("failed to execute process"); + + add_files_from_stdout(&mut files, &git_root, &turbo_root, output.stdout); + + if let Some(from_commit) = from_commit { + let output = Command::new("git") + .arg("diff") + .arg("--name-only") + .arg(format!("{}...{}", from_commit, to_commit)) + .arg("--") + .arg(turbo_root.to_str().unwrap()) + .current_dir(&git_root) + .output() + .expect("failed to execute process"); + + add_files_from_stdout(&mut files, &git_root, &turbo_root, output.stdout); + } + + let output = Command::new("git") + .arg("ls-files") + .arg("--other") + .arg("--exclude-standard") + .arg("--") + .arg(turbo_root.to_str().unwrap()) + .current_dir(&git_root) + .output() + .expect("failed to execute process"); + + add_files_from_stdout(&mut files, &git_root, &turbo_root, output.stdout); + + Ok(files) + } + + // Calls git::changed_files then compares it against the expected version that + // shells out to git + fn changed_files( + git_root: PathBuf, + turbo_root: PathBuf, + from_commit: Option<&str>, + to_commit: &str, + ) -> Result, Error> { + let expected_files = + expected_changed_files(&git_root, &turbo_root, from_commit, to_commit)?; + let files = + super::changed_files(git_root.clone(), turbo_root.clone(), from_commit, to_commit)?; + + assert_eq!(files, expected_files); + + Ok(files) + } + + #[test] + fn test_deleted_files() -> Result<(), Error> { + let (repo_root, repo) = setup_repository()?; + + let file = repo_root.path().join("foo.js"); + let file_path = Path::new("foo.js"); + fs::write(&file, "let z = 0;")?; + + let first_commit_oid = commit_file(&repo, &file_path, None)?; + + fs::remove_file(&file)?; + let _second_commit_oid = commit_delete(&repo, &file_path, first_commit_oid)?; + + let first_commit_sha = first_commit_oid.to_string(); + let git_root = repo_root.path().to_owned(); + let turborepo_root = repo_root.path().to_owned(); + let files = changed_files(git_root, turborepo_root, Some(&first_commit_sha), "HEAD")?; + + assert_eq!(files, HashSet::from(["foo.js".to_string()])); + Ok(()) + } + + #[test] + fn test_merge_base() -> Result<(), Error> { + let (repo_root, repo) = setup_repository()?; + let first_file = repo_root.path().join("foo.js"); + fs::write(&first_file, "let z = 0;")?; + // Create a base commit. This will *not* be the merge base + let first_commit_oid = commit_file(&repo, Path::new("foo.js"), None)?; + + let second_file = repo_root.path().join("bar.js"); + fs::write(&second_file, "let y = 1;")?; + // This commit will be the merge base + let second_commit_oid = commit_file(&repo, Path::new("bar.js"), Some(first_commit_oid))?; + + let third_file = repo_root.path().join("baz.js"); + fs::write(&third_file, "let x = 2;")?; + // Create a first commit off of merge base + let third_commit_oid = commit_file(&repo, Path::new("baz.js"), Some(second_commit_oid))?; + + // Move head back to merge base + repo.set_head_detached(second_commit_oid)?; + let fourth_file = repo_root.path().join("qux.js"); + fs::write(&fourth_file, "let w = 3;")?; + // Create a second commit off of merge base + let fourth_commit_oid = commit_file(&repo, Path::new("qux.js"), Some(second_commit_oid))?; + + repo.set_head_detached(third_commit_oid)?; + let merge_base = repo.merge_base(third_commit_oid, fourth_commit_oid)?; + + assert_eq!(merge_base, second_commit_oid); + + let files = changed_files( + repo_root.path().to_path_buf(), + repo_root.path().to_path_buf(), + Some(&third_commit_oid.to_string()), + &fourth_commit_oid.to_string(), + )?; + + assert_eq!( + files, + HashSet::from(["qux.js".to_string(), "baz.js".to_string()]) + ); + + Ok(()) + } + #[test] fn test_changed_files() -> Result<(), Error> { - let repo_root = tempfile::tempdir()?; - let repo = Repository::init(repo_root.path())?; + let (repo_root, repo) = setup_repository()?; + let mut index = repo.index()?; let turbo_root = repo_root.path(); - let mut config = repo.config()?; - config.set_str("user.name", "test")?; - config.set_str("user.email", "test@example.com")?; let file = repo_root.path().join("foo.js"); fs::write(file, "let z = 0;")?; @@ -200,10 +452,24 @@ mod tests { fs::write(new_file, "let y = 1;")?; // Test that uncommitted file is marked as changed - let files = super::changed_files( + let files = changed_files( + repo_root.path().to_path_buf(), + turbo_root.to_path_buf(), + None, + "HEAD", + )?; + assert_eq!(files, HashSet::from(["bar.js".to_string()])); + + // Add file to index + index.add_path(Path::new("bar.js"))?; + index.write()?; + + // Test that uncommitted file in index is still marked as changed + let files = changed_files( repo_root.path().to_path_buf(), turbo_root.to_path_buf(), None, + "HEAD", )?; assert_eq!(files, HashSet::from(["bar.js".to_string()])); @@ -211,13 +477,11 @@ mod tests { let second_commit_oid = commit_file(&repo, Path::new("bar.js"), Some(first_commit_oid))?; // Test that only second file is marked as changed when we check commit range - let files = super::changed_files( + let files = changed_files( repo_root.path().to_path_buf(), turbo_root.to_path_buf(), - Some(( - first_commit_oid.to_string().as_str(), - second_commit_oid.to_string().as_str(), - )), + Some(first_commit_oid.to_string().as_str()), + second_commit_oid.to_string().as_str(), )?; assert_eq!(files, HashSet::from(["bar.js".to_string()])); @@ -227,13 +491,11 @@ mod tests { fs::write(new_file, "let x = 2;")?; // Test that `turbo_root` filters out files not in the specified directory - let files = super::changed_files( + let files = changed_files( repo_root.path().to_path_buf(), repo_root.path().join("subdir"), - Some(( - first_commit_oid.to_string().as_str(), - second_commit_oid.to_string().as_str(), - )), + Some(first_commit_oid.to_string().as_str()), + second_commit_oid.to_string().as_str(), )?; assert_eq!(files, HashSet::from(["baz.js".to_string()])); @@ -242,11 +504,7 @@ mod tests { #[test] fn test_changed_files_with_root_as_relative() -> Result<(), Error> { - let repo_root = tempfile::tempdir()?; - let repo = Repository::init(repo_root.path())?; - let mut config = repo.config()?; - config.set_str("user.name", "test")?; - config.set_str("user.email", "test@example.com")?; + let (repo_root, repo) = setup_repository()?; let file = repo_root.path().join("foo.js"); fs::write(file, "let z = 0;")?; @@ -259,10 +517,11 @@ mod tests { // Test that uncommitted file is marked as changed with the parameters that Go // will pass - let files = super::changed_files( + let files = changed_files( repo_root.path().to_path_buf(), repo_root.path().to_path_buf(), None, + "HEAD", )?; assert_eq!(files, HashSet::from(["bar.js".to_string()])); @@ -273,11 +532,7 @@ mod tests { // (occurs when the monorepo is nested inside a subdirectory of git repository) #[test] fn test_changed_files_with_subdir_as_turbo_root() -> Result<(), Error> { - let repo_root = tempfile::tempdir()?; - let repo = Repository::init(repo_root.path())?; - let mut config = repo.config()?; - config.set_str("user.name", "test")?; - config.set_str("user.email", "test@example.com")?; + let (repo_root, repo) = setup_repository()?; fs::create_dir(repo_root.path().join("subdir"))?; // Create additional nested directory to test that we return a system path @@ -291,10 +546,11 @@ mod tests { let new_file = repo_root.path().join("subdir").join("src").join("bar.js"); fs::write(new_file, "let y = 1;")?; - let files = super::changed_files( + let files = changed_files( repo_root.path().to_path_buf(), repo_root.path().join("subdir"), None, + "HEAD", )?; #[cfg(unix)] @@ -309,13 +565,11 @@ mod tests { commit_file(&repo, Path::new("subdir/src/bar.js"), Some(first_commit))?; - let files = super::changed_files( + let files = changed_files( repo_root.path().to_path_buf(), repo_root.path().join("subdir"), - Some(( - first_commit.to_string().as_str(), - repo.head()?.peel_to_commit()?.id().to_string().as_str(), - )), + Some(first_commit.to_string().as_str()), + repo.head()?.peel_to_commit()?.id().to_string().as_str(), )?; #[cfg(unix)] @@ -333,11 +587,7 @@ mod tests { #[test] fn test_previous_content() -> Result<(), Error> { - let repo_root = tempfile::tempdir()?; - let repo = Repository::init(repo_root.path())?; - let mut config = repo.config()?; - config.set_str("user.name", "test")?; - config.set_str("user.email", "test@example.com")?; + let (repo_root, repo) = setup_repository()?; let file = repo_root.path().join("foo.js"); fs::write(&file, "let z = 0;")?; @@ -383,11 +633,7 @@ mod tests { #[test] fn test_revparse() -> Result<(), Error> { - let repo_root = tempfile::tempdir()?; - let repo = Repository::init(repo_root.path())?; - let mut config = repo.config()?; - config.set_str("user.name", "test")?; - config.set_str("user.email", "test@example.com")?; + let (repo_root, repo) = setup_repository()?; let file = repo_root.path().join("foo.js"); fs::write(&file, "let z = 0;")?; From aabcae740415094034185c35a533238bb9edafd7 Mon Sep 17 00:00:00 2001 From: --global Date: Mon, 17 Apr 2023 11:17:48 -0400 Subject: [PATCH 02/10] Switching back to git command instead of git2 --- Cargo.lock | 38 ---- cli/internal/scm/git_rust.go | 4 + crates/turborepo-scm/Cargo.toml | 1 - crates/turborepo-scm/src/git.rs | 304 ++++++++------------------------ crates/turborepo-scm/src/lib.rs | 2 - 5 files changed, 75 insertions(+), 274 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 09a88f353b400..859ca63184c34 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -722,9 +722,6 @@ name = "cc" version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" -dependencies = [ - "jobserver", -] [[package]] name = "cesu8" @@ -2290,19 +2287,6 @@ version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4" -[[package]] -name = "git2" -version = "0.16.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccf7f68c2995f392c49fffb4f95ae2c873297830eb25c6bc4c114ce8f4562acc" -dependencies = [ - "bitflags 1.3.2", - "libc", - "libgit2-sys", - "log", - "url", -] - [[package]] name = "glob" version = "0.3.1" @@ -2873,15 +2857,6 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" -[[package]] -name = "jobserver" -version = "0.1.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" -dependencies = [ - "libc", -] - [[package]] name = "jpeg-decoder" version = "0.3.0" @@ -3059,18 +3034,6 @@ version = "0.2.141" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5" -[[package]] -name = "libgit2-sys" -version = "0.14.2+1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f3d95f6b51075fe9810a7ae22c7095f12b98005ab364d8544797a825ce946a4" -dependencies = [ - "cc", - "libc", - "libz-sys", - "pkg-config", -] - [[package]] name = "libloading" version = "0.7.4" @@ -8128,7 +8091,6 @@ version = "0.1.0" dependencies = [ "anyhow", "dunce", - "git2", "tempfile", "thiserror", "turbopath", diff --git a/cli/internal/scm/git_rust.go b/cli/internal/scm/git_rust.go index 7860b6bd48cd9..81cc60c9a38b5 100644 --- a/cli/internal/scm/git_rust.go +++ b/cli/internal/scm/git_rust.go @@ -24,5 +24,9 @@ func (g *git) ChangedFiles(fromCommit string, toCommit string, monorepoRoot stri } func (g *git) PreviousContent(fromCommit string, filePath string) ([]byte, error) { + if fromCommit == "" { + return nil, fmt.Errorf("Need commit sha to inspect file contents") + } + return ffi.PreviousContent(g.repoRoot, fromCommit, filePath) } diff --git a/crates/turborepo-scm/Cargo.toml b/crates/turborepo-scm/Cargo.toml index 9c40f507ce844..2311be38f6cc4 100644 --- a/crates/turborepo-scm/Cargo.toml +++ b/crates/turborepo-scm/Cargo.toml @@ -9,7 +9,6 @@ license = "MPL-2.0" [dependencies] anyhow = { workspace = true } dunce = { workspace = true } -git2 = { version = "0.16.1", default-features = false } thiserror = { workspace = true } turbopath = { workspace = true } diff --git a/crates/turborepo-scm/src/git.rs b/crates/turborepo-scm/src/git.rs index 5adb8bef29ab7..92bdcb1215048 100644 --- a/crates/turborepo-scm/src/git.rs +++ b/crates/turborepo-scm/src/git.rs @@ -1,16 +1,20 @@ use std::{ collections::HashSet, path::{Path, PathBuf}, + process::Command, }; -use git2::{DiffFormat, DiffOptions, Repository}; use turbopath::{AbsoluteSystemPathBuf, AnchoredSystemPathBuf}; use crate::Error; /// Finds the changed files in a repository between index and working directory /// (unstaged changes) and between two commits. Includes untracked files, -/// i.e. files not yet in git +/// i.e. files not yet in git. +/// +/// We shell out to git instead of using a git2 library because git2 doesn't +/// support shallow clones, and therefore errors on repositories that +/// are shallow cloned. /// /// # Arguments /// @@ -27,43 +31,71 @@ pub fn changed_files( from_commit: Option<&str>, to_commit: &str, ) -> Result, Error> { - // Initialize repository at repo root - let repo = Repository::open(&git_root)?; let git_root = AbsoluteSystemPathBuf::new(git_root)?; let turbo_root = AbsoluteSystemPathBuf::new(turbo_root)?; - let anchored_turbo_root = git_root.anchor(&turbo_root)?; let mut files = HashSet::new(); - add_changed_files_from_to_commit_to_working_tree( - &git_root, - &repo, - &anchored_turbo_root, - &turbo_root, - to_commit, - &mut files, - )?; - add_changed_files_from_unstaged_changes( - &git_root, - &repo, - &anchored_turbo_root, - &turbo_root, - &mut files, - )?; + let output = Command::new("git") + .arg("diff") + .arg("--name-only") + .arg(to_commit) + .arg("--") + .arg(turbo_root.to_str().unwrap()) + .current_dir(&git_root) + .output() + .expect("failed to execute process"); + + add_files_from_stdout(&mut files, &git_root, &turbo_root, output.stdout); + if let Some(from_commit) = from_commit { - add_changed_files_from_commits( - &git_root, - &repo, - &anchored_turbo_root, - &turbo_root, - &mut files, - from_commit, - to_commit, - )?; + let output = Command::new("git") + .arg("diff") + .arg("--name-only") + .arg(format!("{}...{}", from_commit, to_commit)) + .arg("--") + .arg(turbo_root.to_str().unwrap()) + .current_dir(&git_root) + .output() + .expect("failed to execute process"); + + add_files_from_stdout(&mut files, &git_root, &turbo_root, output.stdout); } + let output = Command::new("git") + .arg("ls-files") + .arg("--other") + .arg("--exclude-standard") + .arg("--") + .arg(turbo_root.to_str().unwrap()) + .current_dir(&git_root) + .output() + .expect("failed to execute process"); + + add_files_from_stdout(&mut files, &git_root, &turbo_root, output.stdout); + Ok(files) } +fn add_files_from_stdout( + files: &mut HashSet, + git_root: &AbsoluteSystemPathBuf, + turbo_root: &AbsoluteSystemPathBuf, + stdout: Vec, +) { + let stdout = String::from_utf8(stdout).unwrap(); + for line in stdout.lines() { + let path = Path::new(line); + let anchored_to_turbo_root_file_path = + reanchor_path_from_git_root_to_turbo_root(git_root, turbo_root, path).unwrap(); + files.insert( + anchored_to_turbo_root_file_path + .to_str() + .unwrap() + .to_string(), + ); + } +} + fn reanchor_path_from_git_root_to_turbo_root( git_root: &AbsoluteSystemPathBuf, turbo_root: &AbsoluteSystemPathBuf, @@ -75,106 +107,6 @@ fn reanchor_path_from_git_root_to_turbo_root( Ok(anchored_to_turbo_root_file_path) } -// Equivalent of `git diff --name-only -- -fn add_changed_files_from_to_commit_to_working_tree( - git_root: &AbsoluteSystemPathBuf, - repo: &Repository, - anchored_turbo_root: &AnchoredSystemPathBuf, - turbo_root: &AbsoluteSystemPathBuf, - to_commit: &str, - files: &mut HashSet, -) -> Result<(), Error> { - let to_commit_ref = repo.revparse_single(to_commit)?; - let to_commit = to_commit_ref.peel_to_commit()?; - let to_tree = to_commit.tree()?; - let mut options = DiffOptions::new(); - options.pathspec(anchored_turbo_root.to_str()?); - - let diff = repo.diff_tree_to_workdir_with_index(Some(&to_tree), Some(&mut options))?; - - for delta in diff.deltas() { - let file = delta.old_file(); - if let Some(file_path) = file.path() { - let anchored_to_turbo_root_file_path = - reanchor_path_from_git_root_to_turbo_root(git_root, turbo_root, file_path)?; - files.insert(anchored_to_turbo_root_file_path.to_str()?.to_string()); - } - } - - Ok(()) -} - -// Equivalent of `git ls-files --other --exclude-standard -- ` -fn add_changed_files_from_unstaged_changes( - git_root: &AbsoluteSystemPathBuf, - repo: &Repository, - anchored_turbo_root: &AnchoredSystemPathBuf, - turbo_root: &AbsoluteSystemPathBuf, - files: &mut HashSet, -) -> Result<(), Error> { - let mut options = DiffOptions::new(); - options.include_untracked(true); - options.recurse_untracked_dirs(true); - - options.pathspec(anchored_turbo_root.to_str()?); - - let diff = repo.diff_index_to_workdir(None, Some(&mut options))?; - - for delta in diff.deltas() { - let file = delta.old_file(); - if let Some(file_path) = file.path() { - let anchored_to_turbo_root_file_path = - reanchor_path_from_git_root_to_turbo_root(git_root, turbo_root, file_path)?; - files.insert(anchored_to_turbo_root_file_path.to_str()?.to_string()); - } - } - - Ok(()) -} - -// Equivalent of `git diff --name-only ... -- -// ` NOTE: This is **not** the same as -// `git diff --name-only .. -- ` -// (note the triple dots vs double dots) -// The triple dot version is a diff of the most recent common ancestor (merge -// base) of and to , whereas the double dot -// version is a diff of to . -fn add_changed_files_from_commits( - git_root: &AbsoluteSystemPathBuf, - repo: &Repository, - anchored_turbo_root: &AnchoredSystemPathBuf, - turbo_root: &AbsoluteSystemPathBuf, - files: &mut HashSet, - from_commit: &str, - to_commit: &str, -) -> Result<(), Error> { - let from_commit_ref = repo.revparse_single(from_commit)?; - let to_commit_ref = repo.revparse_single(to_commit)?; - let mergebase_of_from_and_to = repo.merge_base(from_commit_ref.id(), to_commit_ref.id())?; - - let mergebase_commit = repo.find_commit(mergebase_of_from_and_to)?; - let to_commit = to_commit_ref.peel_to_commit()?; - let mergebase_tree = mergebase_commit.tree()?; - let to_tree = to_commit.tree()?; - - let mut options = DiffOptions::new(); - options.pathspec(anchored_turbo_root.to_str()?); - - let diff = repo.diff_tree_to_tree(Some(&mergebase_tree), Some(&to_tree), Some(&mut options))?; - diff.print(DiffFormat::NameOnly, |_, _, _| true)?; - - for delta in diff.deltas() { - let file = delta.old_file(); - if let Some(file_path) = file.path() { - let anchored_to_turbo_root_file_path = - reanchor_path_from_git_root_to_turbo_root(git_root, turbo_root, file_path)?; - files.insert(anchored_to_turbo_root_file_path.to_str()?.to_string()); - } - } - - Ok(()) -} - /// Finds the content of a file at a previous commit. Assumes file is in a git /// repository /// @@ -190,20 +122,13 @@ pub fn previous_content( from_commit: &str, file_path: PathBuf, ) -> Result, Error> { - let repo = Repository::open(&git_root)?; - let git_root = AbsoluteSystemPathBuf::new(dunce::canonicalize(git_root)?)?; - let file_path = AbsoluteSystemPathBuf::new(dunce::canonicalize(file_path)?)?; - let from_commit_ref = repo.revparse_single(from_commit)?; - let from_commit = from_commit_ref.peel_to_commit()?; - let from_tree = from_commit.tree()?; - - let relative_path = git_root.anchor(&file_path)?; + let output = Command::new("git") + .arg("show") + .arg(format!("{}:{}", from_commit, file_path.to_str().unwrap())) + .current_dir(&git_root) + .output()?; - let file = from_tree.get_path(relative_path.as_path())?; - let blob = repo.find_blob(file.id())?; - let content = blob.content(); - - Ok(content.to_vec()) + Ok(output.stdout) } #[cfg(test)] @@ -221,7 +146,10 @@ mod tests { use turbopath::AbsoluteSystemPathBuf; use super::previous_content; - use crate::{git::reanchor_path_from_git_root_to_turbo_root, Error}; + use crate::{ + git::{changed_files, reanchor_path_from_git_root_to_turbo_root}, + Error, + }; fn setup_repository() -> Result<(TempDir, Repository), Error> { let repo_root = tempfile::tempdir()?; @@ -279,96 +207,6 @@ mod tests { )?) } - fn add_files_from_stdout( - files: &mut HashSet, - git_root: &AbsoluteSystemPathBuf, - turbo_root: &AbsoluteSystemPathBuf, - stdout: Vec, - ) { - let stdout = String::from_utf8(stdout).unwrap(); - for line in stdout.lines() { - let path = Path::new(line); - let anchored_to_turbo_root_file_path = - reanchor_path_from_git_root_to_turbo_root(git_root, turbo_root, path).unwrap(); - files.insert( - anchored_to_turbo_root_file_path - .to_str() - .unwrap() - .to_string(), - ); - } - } - - // An implementation that shells out to the git command like the old Go version - fn expected_changed_files( - git_root: &Path, - turbo_root: &Path, - from_commit: Option<&str>, - to_commit: &str, - ) -> Result, Error> { - let git_root = AbsoluteSystemPathBuf::new(dunce::canonicalize(git_root)?)?; - let turbo_root = AbsoluteSystemPathBuf::new(dunce::canonicalize(turbo_root)?)?; - - let mut files = HashSet::new(); - let output = Command::new("git") - .arg("diff") - .arg("--name-only") - .arg(to_commit) - .arg("--") - .arg(turbo_root.to_str().unwrap()) - .current_dir(&git_root) - .output() - .expect("failed to execute process"); - - add_files_from_stdout(&mut files, &git_root, &turbo_root, output.stdout); - - if let Some(from_commit) = from_commit { - let output = Command::new("git") - .arg("diff") - .arg("--name-only") - .arg(format!("{}...{}", from_commit, to_commit)) - .arg("--") - .arg(turbo_root.to_str().unwrap()) - .current_dir(&git_root) - .output() - .expect("failed to execute process"); - - add_files_from_stdout(&mut files, &git_root, &turbo_root, output.stdout); - } - - let output = Command::new("git") - .arg("ls-files") - .arg("--other") - .arg("--exclude-standard") - .arg("--") - .arg(turbo_root.to_str().unwrap()) - .current_dir(&git_root) - .output() - .expect("failed to execute process"); - - add_files_from_stdout(&mut files, &git_root, &turbo_root, output.stdout); - - Ok(files) - } - - // Calls git::changed_files then compares it against the expected version that - // shells out to git - fn changed_files( - git_root: PathBuf, - turbo_root: PathBuf, - from_commit: Option<&str>, - to_commit: &str, - ) -> Result, Error> { - let expected_files = - expected_changed_files(&git_root, &turbo_root, from_commit, to_commit)?; - let files = - super::changed_files(git_root.clone(), turbo_root.clone(), from_commit, to_commit)?; - - assert_eq!(files, expected_files); - - Ok(files) - } - #[test] fn test_deleted_files() -> Result<(), Error> { let (repo_root, repo) = setup_repository()?; diff --git a/crates/turborepo-scm/src/lib.rs b/crates/turborepo-scm/src/lib.rs index 60456ffe4542d..2e313fde4923c 100644 --- a/crates/turborepo-scm/src/lib.rs +++ b/crates/turborepo-scm/src/lib.rs @@ -5,8 +5,6 @@ pub mod git; #[derive(Debug, Error)] pub enum Error { - #[error("git error: {0}")] - Git(#[from] git2::Error), #[error("repository not found")] RepositoryNotFound, #[error("io error: {0}")] From 9b2e84852e6b00c13369533b40731dc26e1a06fa Mon Sep 17 00:00:00 2001 From: --global Date: Mon, 17 Apr 2023 11:50:46 -0400 Subject: [PATCH 03/10] Made sure relativeTo is an anchored path at git root --- cli/internal/scm/git_rust.go | 1 + crates/turborepo-scm/src/git.rs | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/cli/internal/scm/git_rust.go b/cli/internal/scm/git_rust.go index 81cc60c9a38b5..bce51a87916d8 100644 --- a/cli/internal/scm/git_rust.go +++ b/cli/internal/scm/git_rust.go @@ -10,6 +10,7 @@ package scm import ( + "fmt" "github.com/vercel/turbo/cli/internal/ffi" ) diff --git a/crates/turborepo-scm/src/git.rs b/crates/turborepo-scm/src/git.rs index 92bdcb1215048..6080e1628ff45 100644 --- a/crates/turborepo-scm/src/git.rs +++ b/crates/turborepo-scm/src/git.rs @@ -33,6 +33,7 @@ pub fn changed_files( ) -> Result, Error> { let git_root = AbsoluteSystemPathBuf::new(git_root)?; let turbo_root = AbsoluteSystemPathBuf::new(turbo_root)?; + let turbo_root_relative_to_git_root = git_root.anchor(&turbo_root)?; let mut files = HashSet::new(); let output = Command::new("git") @@ -40,7 +41,7 @@ pub fn changed_files( .arg("--name-only") .arg(to_commit) .arg("--") - .arg(turbo_root.to_str().unwrap()) + .arg(turbo_root_relative_to_git_root.to_str().unwrap()) .current_dir(&git_root) .output() .expect("failed to execute process"); @@ -53,7 +54,7 @@ pub fn changed_files( .arg("--name-only") .arg(format!("{}...{}", from_commit, to_commit)) .arg("--") - .arg(turbo_root.to_str().unwrap()) + .arg(turbo_root_relative_to_git_root.to_str().unwrap()) .current_dir(&git_root) .output() .expect("failed to execute process"); @@ -66,7 +67,7 @@ pub fn changed_files( .arg("--other") .arg("--exclude-standard") .arg("--") - .arg(turbo_root.to_str().unwrap()) + .arg(turbo_root_relative_to_git_root.to_str().unwrap()) .current_dir(&git_root) .output() .expect("failed to execute process"); From d7c0598eafe955f92c7e292d2b836e5699c684ba Mon Sep 17 00:00:00 2001 From: --global Date: Mon, 17 Apr 2023 11:59:15 -0400 Subject: [PATCH 04/10] Added test for shallow cloned repo --- Cargo.lock | 38 ++++++++++++++++++++++++++++++++ crates/turborepo-scm/Cargo.toml | 1 + crates/turborepo-scm/src/git.rs | 39 ++++++++++++++++++++++++++++----- crates/turborepo-scm/src/lib.rs | 2 ++ 4 files changed, 75 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 859ca63184c34..09a88f353b400 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -722,6 +722,9 @@ name = "cc" version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" +dependencies = [ + "jobserver", +] [[package]] name = "cesu8" @@ -2287,6 +2290,19 @@ version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4" +[[package]] +name = "git2" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf7f68c2995f392c49fffb4f95ae2c873297830eb25c6bc4c114ce8f4562acc" +dependencies = [ + "bitflags 1.3.2", + "libc", + "libgit2-sys", + "log", + "url", +] + [[package]] name = "glob" version = "0.3.1" @@ -2857,6 +2873,15 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" +[[package]] +name = "jobserver" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" +dependencies = [ + "libc", +] + [[package]] name = "jpeg-decoder" version = "0.3.0" @@ -3034,6 +3059,18 @@ version = "0.2.141" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5" +[[package]] +name = "libgit2-sys" +version = "0.14.2+1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f3d95f6b51075fe9810a7ae22c7095f12b98005ab364d8544797a825ce946a4" +dependencies = [ + "cc", + "libc", + "libz-sys", + "pkg-config", +] + [[package]] name = "libloading" version = "0.7.4" @@ -8091,6 +8128,7 @@ version = "0.1.0" dependencies = [ "anyhow", "dunce", + "git2", "tempfile", "thiserror", "turbopath", diff --git a/crates/turborepo-scm/Cargo.toml b/crates/turborepo-scm/Cargo.toml index 2311be38f6cc4..01e3f7df07fc7 100644 --- a/crates/turborepo-scm/Cargo.toml +++ b/crates/turborepo-scm/Cargo.toml @@ -13,4 +13,5 @@ thiserror = { workspace = true } turbopath = { workspace = true } [dev-dependencies] +git2 = { version = "0.16.1", default-features = false } tempfile = { workspace = true } diff --git a/crates/turborepo-scm/src/git.rs b/crates/turborepo-scm/src/git.rs index 6080e1628ff45..9b0241fd296b0 100644 --- a/crates/turborepo-scm/src/git.rs +++ b/crates/turborepo-scm/src/git.rs @@ -144,13 +144,9 @@ mod tests { use git2::{Oid, Repository}; use tempfile::TempDir; - use turbopath::AbsoluteSystemPathBuf; use super::previous_content; - use crate::{ - git::{changed_files, reanchor_path_from_git_root_to_turbo_root}, - Error, - }; + use crate::{git::changed_files, Error}; fn setup_repository() -> Result<(TempDir, Repository), Error> { let repo_root = tempfile::tempdir()?; @@ -208,6 +204,39 @@ mod tests { )?) } + #[test] + fn test_shallow_clone() -> Result<(), Error> { + let tmp_dir = tempfile::tempdir()?; + let output = Command::new("git") + .args(&[ + "clone", + "--depth", + "2", + "https://github.com/vercel/app-playground.git", + tmp_dir.path().to_str().unwrap(), + ]) + .output()?; + assert!(output.status.success()); + + assert!(changed_files( + tmp_dir.path().to_owned(), + tmp_dir.path().to_owned(), + Some("HEAD~1"), + "HEAD", + ) + .is_ok()); + + assert!(changed_files( + tmp_dir.path().to_owned(), + tmp_dir.path().to_owned(), + None, + "HEAD", + ) + .is_ok()); + + Ok(()) + } + #[test] fn test_deleted_files() -> Result<(), Error> { let (repo_root, repo) = setup_repository()?; diff --git a/crates/turborepo-scm/src/lib.rs b/crates/turborepo-scm/src/lib.rs index 2e313fde4923c..60456ffe4542d 100644 --- a/crates/turborepo-scm/src/lib.rs +++ b/crates/turborepo-scm/src/lib.rs @@ -5,6 +5,8 @@ pub mod git; #[derive(Debug, Error)] pub enum Error { + #[error("git error: {0}")] + Git(#[from] git2::Error), #[error("repository not found")] RepositoryNotFound, #[error("io error: {0}")] From b04fb21197e2c10016a7abd0400d7fed4243b093 Mon Sep 17 00:00:00 2001 From: --global Date: Mon, 17 Apr 2023 15:39:31 -0400 Subject: [PATCH 05/10] Fixing tests and specifying paths more carefully --- cli/internal/scm/git_go.go | 5 +- cli/internal/scm/git_rust.go | 7 +- cli/internal/scm/scm.go | 11 +-- crates/turborepo-scm/Cargo.toml | 2 +- crates/turborepo-scm/src/git.rs | 126 +++++++++++++++++++------------- crates/turborepo-scm/src/lib.rs | 4 +- 6 files changed, 92 insertions(+), 63 deletions(-) diff --git a/cli/internal/scm/git_go.go b/cli/internal/scm/git_go.go index 1e5060895f0bc..0dac2bfdb074b 100644 --- a/cli/internal/scm/git_go.go +++ b/cli/internal/scm/git_go.go @@ -11,6 +11,7 @@ package scm import ( "fmt" + "github.com/vercel/turbo/cli/internal/turbopath" "os/exec" "path/filepath" "strings" @@ -20,13 +21,13 @@ import ( // git implements operations on a git repository. type git struct { - repoRoot string + repoRoot turbopath.AbsoluteSystemPath } // ChangedFiles returns a list of modified files since the given commit, optionally including untracked files. func (g *git) ChangedFiles(fromCommit string, toCommit string, relativeTo string) ([]string, error) { if relativeTo == "" { - relativeTo = g.repoRoot + relativeTo = g.repoRoot.ToString() } relSuffix := []string{"--", relativeTo} command := []string{"diff", "--name-only", toCommit} diff --git a/cli/internal/scm/git_rust.go b/cli/internal/scm/git_rust.go index bce51a87916d8..4b4cd2dedc1b0 100644 --- a/cli/internal/scm/git_rust.go +++ b/cli/internal/scm/git_rust.go @@ -12,16 +12,17 @@ package scm import ( "fmt" "github.com/vercel/turbo/cli/internal/ffi" + "github.com/vercel/turbo/cli/internal/turbopath" ) // git implements operations on a git repository. type git struct { - repoRoot string + repoRoot turbopath.AbsoluteSystemPath } // ChangedFiles returns a list of modified files since the given commit, optionally including untracked files. func (g *git) ChangedFiles(fromCommit string, toCommit string, monorepoRoot string) ([]string, error) { - return ffi.ChangedFiles(g.repoRoot, monorepoRoot, fromCommit, toCommit) + return ffi.ChangedFiles(g.repoRoot.ToString(), monorepoRoot, fromCommit, toCommit) } func (g *git) PreviousContent(fromCommit string, filePath string) ([]byte, error) { @@ -29,5 +30,5 @@ func (g *git) PreviousContent(fromCommit string, filePath string) ([]byte, error return nil, fmt.Errorf("Need commit sha to inspect file contents") } - return ffi.PreviousContent(g.repoRoot, fromCommit, filePath) + return ffi.PreviousContent(g.repoRoot.ToString(), fromCommit, filePath) } diff --git a/cli/internal/scm/scm.go b/cli/internal/scm/scm.go index b491cd8d382ff..e7f17c8b7374a 100644 --- a/cli/internal/scm/scm.go +++ b/cli/internal/scm/scm.go @@ -7,11 +7,8 @@ package scm import ( - "path/filepath" - "github.com/pkg/errors" - "github.com/vercel/turbo/cli/internal/fs" "github.com/vercel/turbo/cli/internal/turbopath" ) @@ -27,8 +24,8 @@ type SCM interface { // newGitSCM returns a new SCM instance for this repo root. // It returns nil if there is no known implementation there. -func newGitSCM(repoRoot string) SCM { - if fs.PathExists(filepath.Join(repoRoot, ".git")) { +func newGitSCM(repoRoot turbopath.AbsoluteSystemPath) SCM { + if repoRoot.UntypedJoin(".git").Exists() { return &git{repoRoot: repoRoot} } return nil @@ -36,7 +33,7 @@ func newGitSCM(repoRoot string) SCM { // newFallback returns a new SCM instance for this repo root. // If there is no known implementation it returns a stub. -func newFallback(repoRoot string) (SCM, error) { +func newFallback(repoRoot turbopath.AbsoluteSystemPath) (SCM, error) { if scm := newGitSCM(repoRoot); scm != nil { return scm, nil } @@ -52,5 +49,5 @@ func FromInRepo(repoRoot turbopath.AbsoluteSystemPath) (SCM, error) { if err != nil { return nil, err } - return newFallback(dotGitDir.Dir().ToStringDuringMigration()) + return newFallback(dotGitDir.Dir()) } diff --git a/crates/turborepo-scm/Cargo.toml b/crates/turborepo-scm/Cargo.toml index 01e3f7df07fc7..9c40f507ce844 100644 --- a/crates/turborepo-scm/Cargo.toml +++ b/crates/turborepo-scm/Cargo.toml @@ -9,9 +9,9 @@ license = "MPL-2.0" [dependencies] anyhow = { workspace = true } dunce = { workspace = true } +git2 = { version = "0.16.1", default-features = false } thiserror = { workspace = true } turbopath = { workspace = true } [dev-dependencies] -git2 = { version = "0.16.1", default-features = false } tempfile = { workspace = true } diff --git a/crates/turborepo-scm/src/git.rs b/crates/turborepo-scm/src/git.rs index 9b0241fd296b0..e6676757b4043 100644 --- a/crates/turborepo-scm/src/git.rs +++ b/crates/turborepo-scm/src/git.rs @@ -34,49 +34,65 @@ pub fn changed_files( let git_root = AbsoluteSystemPathBuf::new(git_root)?; let turbo_root = AbsoluteSystemPathBuf::new(turbo_root)?; let turbo_root_relative_to_git_root = git_root.anchor(&turbo_root)?; + let pathspec = turbo_root_relative_to_git_root.to_str()?; let mut files = HashSet::new(); - let output = Command::new("git") - .arg("diff") - .arg("--name-only") - .arg(to_commit) - .arg("--") - .arg(turbo_root_relative_to_git_root.to_str().unwrap()) - .current_dir(&git_root) - .output() - .expect("failed to execute process"); - - add_files_from_stdout(&mut files, &git_root, &turbo_root, output.stdout); + + let output = execute_git_command(&git_root, &["diff", "--name-only", to_commit], pathspec)?; + + add_files_from_stdout(&mut files, &git_root, &turbo_root, output); if let Some(from_commit) = from_commit { - let output = Command::new("git") - .arg("diff") - .arg("--name-only") - .arg(format!("{}...{}", from_commit, to_commit)) - .arg("--") - .arg(turbo_root_relative_to_git_root.to_str().unwrap()) - .current_dir(&git_root) - .output() - .expect("failed to execute process"); - - add_files_from_stdout(&mut files, &git_root, &turbo_root, output.stdout); + let output = execute_git_command( + &git_root, + &[ + "diff", + "--name-only", + &format!("{}...{}", from_commit, to_commit), + ], + pathspec, + )?; + + add_files_from_stdout(&mut files, &git_root, &turbo_root, output); } - let output = Command::new("git") - .arg("ls-files") - .arg("--other") - .arg("--exclude-standard") - .arg("--") - .arg(turbo_root_relative_to_git_root.to_str().unwrap()) - .current_dir(&git_root) - .output() - .expect("failed to execute process"); + let output = execute_git_command( + &git_root, + &["ls-files", "--others", "--exclude-standard"], + pathspec, + )?; - add_files_from_stdout(&mut files, &git_root, &turbo_root, output.stdout); + add_files_from_stdout(&mut files, &git_root, &turbo_root, output); Ok(files) } +fn execute_git_command( + git_root: &AbsoluteSystemPathBuf, + args: &[&str], + pathspec: &str, +) -> Result, Error> { + let mut command = Command::new("git"); + command.args(args).current_dir(&git_root); + + add_pathspec(&mut command, pathspec); + + let output = command.output()?; + + if !output.status.success() { + let stderr = String::from_utf8(output.stderr).unwrap(); + Err(Error::Git(stderr)) + } else { + Ok(output.stdout) + } +} + +fn add_pathspec(command: &mut Command, pathspec: &str) { + if pathspec != "" { + command.arg("--").arg(pathspec); + } +} + fn add_files_from_stdout( files: &mut HashSet, git_root: &AbsoluteSystemPathBuf, @@ -123,20 +139,42 @@ pub fn previous_content( from_commit: &str, file_path: PathBuf, ) -> Result, Error> { - let output = Command::new("git") - .arg("show") - .arg(format!("{}:{}", from_commit, file_path.to_str().unwrap())) - .current_dir(&git_root) - .output()?; + // If git root is not absolute, we error. + let git_root = AbsoluteSystemPathBuf::new(git_root)?; - Ok(output.stdout) + // However for file path we handle both absolute and relative paths + // Note that we assume any relative file path is relative to the git root + let anchored_file_path = if file_path.is_absolute() { + let absolute_file_path = AbsoluteSystemPathBuf::new(file_path)?; + git_root.anchor(&absolute_file_path)? + } else { + file_path.as_path().try_into()? + }; + + let mut command = Command::new("git"); + let command = command + .arg("show") + .arg(format!( + "{}:{}", + from_commit, + anchored_file_path.to_str().unwrap() + )) + .current_dir(&git_root); + + let output = command.output()?; + if output.status.success() { + Ok(output.stdout) + } else { + Err(Error::Git( + String::from_utf8_lossy(&output.stderr).to_string(), + )) + } } #[cfg(test)] mod tests { use std::{ collections::HashSet, - env::set_current_dir, fs, path::{Path, PathBuf}, process::Command, @@ -479,16 +517,6 @@ mod tests { )?; assert_eq!(content, b"let z = 1;"); - set_current_dir(repo_root.path())?; - - // Check that relative paths work as well - let content = previous_content( - PathBuf::from("."), - second_commit_oid.to_string().as_str(), - PathBuf::from("./foo.js"), - )?; - assert_eq!(content, b"let z = 1;"); - let content = previous_content( repo_root.path().to_path_buf(), second_commit_oid.to_string().as_str(), diff --git a/crates/turborepo-scm/src/lib.rs b/crates/turborepo-scm/src/lib.rs index 60456ffe4542d..a93e84721a25f 100644 --- a/crates/turborepo-scm/src/lib.rs +++ b/crates/turborepo-scm/src/lib.rs @@ -6,7 +6,9 @@ pub mod git; #[derive(Debug, Error)] pub enum Error { #[error("git error: {0}")] - Git(#[from] git2::Error), + Git2(#[from] git2::Error), + #[error("git error: {0}")] + Git(String), #[error("repository not found")] RepositoryNotFound, #[error("io error: {0}")] From 8af6886c5bf0504686d4d81a7d5d7ded38abded7 Mon Sep 17 00:00:00 2001 From: --global Date: Tue, 18 Apr 2023 10:46:13 -0400 Subject: [PATCH 06/10] More tests --- crates/turborepo-scm/src/git.rs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/crates/turborepo-scm/src/git.rs b/crates/turborepo-scm/src/git.rs index e6676757b4043..f87b7b7aab6f5 100644 --- a/crates/turborepo-scm/src/git.rs +++ b/crates/turborepo-scm/src/git.rs @@ -499,8 +499,10 @@ mod tests { fs::write(&file, "let z = 0;")?; let first_commit_oid = commit_file(&repo, Path::new("foo.js"), None)?; + println!("first commit: {}", first_commit_oid); fs::write(&file, "let z = 1;")?; let second_commit_oid = commit_file(&repo, Path::new("foo.js"), Some(first_commit_oid))?; + println!("second commit: {}", second_commit_oid); let content = previous_content( repo_root.path().to_path_buf(), @@ -543,6 +545,31 @@ mod tests { let revparsed_head_minus_1 = repo.revparse_single("HEAD~1")?; assert_eq!(revparsed_head_minus_1.id(), first_commit_oid); + let files = changed_files( + repo_root.path().to_path_buf(), + repo_root.path().to_path_buf(), + Some("HEAD^"), + "HEAD", + )?; + assert_eq!(files, HashSet::from(["foo.js".to_string()])); + + let content = previous_content(repo_root.path().to_path_buf(), "HEAD^", file)?; + assert_eq!(content, b"let z = 0;"); + + let new_file = repo_root.path().join("bar.js"); + fs::write(&new_file, "let y = 0;")?; + let third_commit_oid = commit_file(&repo, Path::new("bar.js"), Some(second_commit_oid))?; + let third_commit = repo.find_commit(third_commit_oid)?; + repo.branch("release-1", &third_commit, false)?; + + let files = changed_files( + repo_root.path().to_path_buf(), + repo_root.path().to_path_buf(), + Some("HEAD~1"), + "release-1", + )?; + assert_eq!(files, HashSet::from(["bar.js".to_string()])); + Ok(()) } } From 41dda94d5df337b28dde29cd26d682f7050b8de9 Mon Sep 17 00:00:00 2001 From: --global Date: Tue, 18 Apr 2023 11:21:41 -0400 Subject: [PATCH 07/10] Add backtrace --- crates/turborepo-scm/src/git.rs | 4 +++- crates/turborepo-scm/src/lib.rs | 18 +++++++++++++----- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/crates/turborepo-scm/src/git.rs b/crates/turborepo-scm/src/git.rs index f87b7b7aab6f5..276cabd7d3fd3 100644 --- a/crates/turborepo-scm/src/git.rs +++ b/crates/turborepo-scm/src/git.rs @@ -1,4 +1,5 @@ use std::{ + backtrace::Backtrace, collections::HashSet, path::{Path, PathBuf}, process::Command, @@ -81,7 +82,7 @@ fn execute_git_command( if !output.status.success() { let stderr = String::from_utf8(output.stderr).unwrap(); - Err(Error::Git(stderr)) + Err(Error::Git(stderr, Backtrace::capture())) } else { Ok(output.stdout) } @@ -167,6 +168,7 @@ pub fn previous_content( } else { Err(Error::Git( String::from_utf8_lossy(&output.stderr).to_string(), + Backtrace::capture(), )) } } diff --git a/crates/turborepo-scm/src/lib.rs b/crates/turborepo-scm/src/lib.rs index a93e84721a25f..db94f86df5332 100644 --- a/crates/turborepo-scm/src/lib.rs +++ b/crates/turborepo-scm/src/lib.rs @@ -1,3 +1,8 @@ +#![feature(error_generic_member_access)] +#![feature(provide_any)] + +use std::backtrace; + use thiserror::Error; use turbopath::PathValidationError; @@ -6,13 +11,16 @@ pub mod git; #[derive(Debug, Error)] pub enum Error { #[error("git error: {0}")] - Git2(#[from] git2::Error), + Git2(#[from] git2::Error, #[backtrace] backtrace::Backtrace), #[error("git error: {0}")] - Git(String), + Git(String, #[backtrace] backtrace::Backtrace), #[error("repository not found")] - RepositoryNotFound, + RepositoryNotFound(#[backtrace] backtrace::Backtrace), #[error("io error: {0}")] - Io(#[from] std::io::Error), + Io(#[from] std::io::Error, #[backtrace] backtrace::Backtrace), #[error("path error: {0}")] - Path(#[from] PathValidationError), + Path( + #[from] PathValidationError, + #[backtrace] backtrace::Backtrace, + ), } From d153d5df97445b920170bd6b740b0b29428ef8e5 Mon Sep 17 00:00:00 2001 From: --global Date: Tue, 18 Apr 2023 11:50:35 -0400 Subject: [PATCH 08/10] Potential fix --- crates/turborepo-scm/src/git.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/turborepo-scm/src/git.rs b/crates/turborepo-scm/src/git.rs index 276cabd7d3fd3..79032e254e4e1 100644 --- a/crates/turborepo-scm/src/git.rs +++ b/crates/turborepo-scm/src/git.rs @@ -524,7 +524,7 @@ mod tests { let content = previous_content( repo_root.path().to_path_buf(), second_commit_oid.to_string().as_str(), - PathBuf::from("./foo.js"), + PathBuf::from("foo.js"), )?; assert_eq!(content, b"let z = 1;"); From 404eecef4fb288eb7ef934e7b6c5cdfd0c513c4b Mon Sep 17 00:00:00 2001 From: --global Date: Tue, 18 Apr 2023 12:35:23 -0400 Subject: [PATCH 09/10] Removed print statements --- crates/turborepo-scm/src/git.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/crates/turborepo-scm/src/git.rs b/crates/turborepo-scm/src/git.rs index 79032e254e4e1..a3b0475b38f68 100644 --- a/crates/turborepo-scm/src/git.rs +++ b/crates/turborepo-scm/src/git.rs @@ -501,10 +501,8 @@ mod tests { fs::write(&file, "let z = 0;")?; let first_commit_oid = commit_file(&repo, Path::new("foo.js"), None)?; - println!("first commit: {}", first_commit_oid); fs::write(&file, "let z = 1;")?; let second_commit_oid = commit_file(&repo, Path::new("foo.js"), Some(first_commit_oid))?; - println!("second commit: {}", second_commit_oid); let content = previous_content( repo_root.path().to_path_buf(), From 17b09f9068d7bb219b68dde1ec9d74363fe45dd2 Mon Sep 17 00:00:00 2001 From: --global Date: Tue, 18 Apr 2023 12:47:30 -0400 Subject: [PATCH 10/10] Add error tests --- crates/turborepo-scm/src/git.rs | 48 +++++++++++++++++++++++++++++++++ crates/turborepo-scm/src/lib.rs | 3 +-- 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/crates/turborepo-scm/src/git.rs b/crates/turborepo-scm/src/git.rs index a3b0475b38f68..a4686f00ecf75 100644 --- a/crates/turborepo-scm/src/git.rs +++ b/crates/turborepo-scm/src/git.rs @@ -176,6 +176,7 @@ pub fn previous_content( #[cfg(test)] mod tests { use std::{ + assert_matches::assert_matches, collections::HashSet, fs, path::{Path, PathBuf}, @@ -184,6 +185,7 @@ mod tests { use git2::{Oid, Repository}; use tempfile::TempDir; + use turbopath::PathValidationError; use super::previous_content; use crate::{git::changed_files, Error}; @@ -572,4 +574,50 @@ mod tests { Ok(()) } + + #[test] + fn test_error_cases() -> Result<(), Error> { + let repo_dir = tempfile::tempdir()?; + let repo_does_not_exist = changed_files( + repo_dir.path().to_path_buf(), + repo_dir.path().to_path_buf(), + None, + "HEAD", + ); + + assert_matches!(repo_does_not_exist, Err(Error::Git(_, _))); + + let (repo_root, _repo) = setup_repository()?; + + let commit_does_not_exist = changed_files( + repo_root.path().to_path_buf(), + repo_root.path().to_path_buf(), + None, + "does-not-exist", + ); + + assert_matches!(commit_does_not_exist, Err(Error::Git(_, _))); + + let file_does_not_exist = previous_content( + repo_root.path().to_path_buf(), + "HEAD", + repo_root.path().join("does-not-exist"), + ); + assert_matches!(file_does_not_exist, Err(Error::Git(_, _))); + + let turbo_root = tempfile::tempdir()?; + let turbo_root_is_not_subdir_of_git_root = changed_files( + repo_root.path().to_path_buf(), + turbo_root.path().to_path_buf(), + None, + "HEAD", + ); + + assert_matches!( + turbo_root_is_not_subdir_of_git_root, + Err(Error::Path(PathValidationError::NotParent(_, _), _)) + ); + + Ok(()) + } } diff --git a/crates/turborepo-scm/src/lib.rs b/crates/turborepo-scm/src/lib.rs index db94f86df5332..0f4328f6a5591 100644 --- a/crates/turborepo-scm/src/lib.rs +++ b/crates/turborepo-scm/src/lib.rs @@ -1,5 +1,6 @@ #![feature(error_generic_member_access)] #![feature(provide_any)] +#![feature(assert_matches)] use std::backtrace; @@ -14,8 +15,6 @@ pub enum Error { Git2(#[from] git2::Error, #[backtrace] backtrace::Backtrace), #[error("git error: {0}")] Git(String, #[backtrace] backtrace::Backtrace), - #[error("repository not found")] - RepositoryNotFound(#[backtrace] backtrace::Backtrace), #[error("io error: {0}")] Io(#[from] std::io::Error, #[backtrace] backtrace::Backtrace), #[error("path error: {0}")]