From 1cbba8332911944489962bcde2155fd23f16d12a Mon Sep 17 00:00:00 2001 From: Nicholas Yang Date: Tue, 11 Apr 2023 16:56:30 -0400 Subject: [PATCH] fix(turborepo): SCM tests and renaming (#4462) ### Description Added more testing to SCM and renamed variables to be more understandable (repo_root -> git_root, monorepo_root -> turbo_root) ### Testing Instructions Added tests on Rust side for deleting files and adding file to index. --- 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;")?;