mirror of https://github.com/go-gitea/gitea
* Implemented calling git diff-tree * Ensures wrapper function is called with valid arguments * Parses output into go struct, using strong typing when possible * Modifies services/gitdiff/testdata/acedemic-module * Makes it a bare repo * Adds a branch which updates readme * Adds a branch which updates the webpack configpull/33369/head
parent
77d14fb6d3
commit
a02be75be6
@ -0,0 +1,229 @@ |
|||||||
|
// Copyright 2025 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package gitdiff |
||||||
|
|
||||||
|
import ( |
||||||
|
"bufio" |
||||||
|
"context" |
||||||
|
"fmt" |
||||||
|
"io" |
||||||
|
"strings" |
||||||
|
|
||||||
|
"code.gitea.io/gitea/modules/git" |
||||||
|
"code.gitea.io/gitea/modules/log" |
||||||
|
) |
||||||
|
|
||||||
|
type DiffTree struct { |
||||||
|
Files []*DiffTreeRecord |
||||||
|
} |
||||||
|
|
||||||
|
type DiffTreeRecord struct { |
||||||
|
// Status is one of 'added', 'deleted', 'modified', 'renamed', 'copied', 'typechanged', 'unmerged', 'unknown'
|
||||||
|
Status string |
||||||
|
|
||||||
|
HeadPath string |
||||||
|
BasePath string |
||||||
|
HeadMode git.EntryMode |
||||||
|
BaseMode git.EntryMode |
||||||
|
HeadBlobID string |
||||||
|
BaseBlobID string |
||||||
|
} |
||||||
|
|
||||||
|
// GetDiffTree returns the list of path of the files that have changed between the two commits.
|
||||||
|
// If useMergeBase is true, the diff will be calculated using the merge base of the two commits.
|
||||||
|
// This is the same behavior as using a three-dot diff in git diff.
|
||||||
|
func GetDiffTree(ctx context.Context, gitRepo *git.Repository, useMergeBase bool, baseSha, headSha string) (*DiffTree, error) { |
||||||
|
gitDiffTreeRecords, err := runGitDiffTree(ctx, gitRepo, useMergeBase, baseSha, headSha) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
|
||||||
|
return &DiffTree{ |
||||||
|
Files: gitDiffTreeRecords, |
||||||
|
}, nil |
||||||
|
} |
||||||
|
|
||||||
|
func runGitDiffTree(ctx context.Context, gitRepo *git.Repository, useMergeBase bool, baseSha, headSha string) ([]*DiffTreeRecord, error) { |
||||||
|
useMergeBase, baseCommitID, headCommitID, err := validateGitDiffTreeArguments(gitRepo, useMergeBase, baseSha, headSha) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
|
||||||
|
cmd := git.NewCommand(ctx, "diff-tree", "--raw", "-r", "--find-renames", "--root") |
||||||
|
if useMergeBase { |
||||||
|
cmd.AddArguments("--merge-base") |
||||||
|
} |
||||||
|
cmd.AddDynamicArguments(baseCommitID, headCommitID) |
||||||
|
stdout, _, runErr := cmd.RunStdString(&git.RunOpts{Dir: gitRepo.Path}) |
||||||
|
if runErr != nil { |
||||||
|
log.Warn("git diff-tree: %v", runErr) |
||||||
|
return nil, runErr |
||||||
|
} |
||||||
|
|
||||||
|
return parseGitDiffTree(strings.NewReader(stdout)) |
||||||
|
} |
||||||
|
|
||||||
|
func validateGitDiffTreeArguments(gitRepo *git.Repository, useMergeBase bool, baseSha, headSha string) (bool, string, string, error) { |
||||||
|
// if the head is empty its an error
|
||||||
|
if headSha == "" { |
||||||
|
return false, "", "", fmt.Errorf("headSha is empty") |
||||||
|
} |
||||||
|
|
||||||
|
// if the head commit doesn't exist its and error
|
||||||
|
headCommit, err := gitRepo.GetCommit(headSha) |
||||||
|
if err != nil { |
||||||
|
return false, "", "", fmt.Errorf("failed to get commit headSha: %v", err) |
||||||
|
} |
||||||
|
headCommitID := headCommit.ID.String() |
||||||
|
|
||||||
|
// if the base is empty we should use the parent of the head commit
|
||||||
|
if baseSha == "" { |
||||||
|
// if the headCommit has no parent we should use an empty commit
|
||||||
|
// this can happen when we are generating a diff against an orphaned commit
|
||||||
|
if headCommit.ParentCount() == 0 { |
||||||
|
objectFormat, err := gitRepo.GetObjectFormat() |
||||||
|
if err != nil { |
||||||
|
return false, "", "", err |
||||||
|
} |
||||||
|
|
||||||
|
// We set use merge base to false because we have no base commit
|
||||||
|
return false, objectFormat.EmptyTree().String(), headCommitID, nil |
||||||
|
} |
||||||
|
|
||||||
|
baseCommit, err := headCommit.Parent(0) |
||||||
|
if err != nil { |
||||||
|
return false, "", "", fmt.Errorf("baseSha is '', attempted to use parent of commit %s, got error: %v", headCommit.ID.String(), err) |
||||||
|
} |
||||||
|
return useMergeBase, baseCommit.ID.String(), headCommitID, nil |
||||||
|
} |
||||||
|
|
||||||
|
// try and get the base commit
|
||||||
|
baseCommit, err := gitRepo.GetCommit(baseSha) |
||||||
|
// propagate the error if we couldn't get the base commit
|
||||||
|
if err != nil { |
||||||
|
return useMergeBase, "", "", fmt.Errorf("failed to get base commit %s: %v", baseSha, err) |
||||||
|
} |
||||||
|
|
||||||
|
return useMergeBase, baseCommit.ID.String(), headCommit.ID.String(), nil |
||||||
|
} |
||||||
|
|
||||||
|
func parseGitDiffTree(gitOutput io.Reader) ([]*DiffTreeRecord, error) { |
||||||
|
/* |
||||||
|
The output of `git diff-tree --raw -r --find-renames` is of the form: |
||||||
|
|
||||||
|
:<old_mode> <new_mode> <old_sha> <new_sha> <status>\t<path> |
||||||
|
|
||||||
|
or for renames: |
||||||
|
|
||||||
|
:<old_mode> <new_mode> <old_sha> <new_sha> <status>\t<old_path>\t<new_path> |
||||||
|
|
||||||
|
See: <https://git-scm.com/docs/git-diff-tree#_raw_output_format> for more details
|
||||||
|
*/ |
||||||
|
results := make([]*DiffTreeRecord, 0) |
||||||
|
|
||||||
|
lines := bufio.NewScanner(gitOutput) |
||||||
|
for lines.Scan() { |
||||||
|
line := lines.Text() |
||||||
|
|
||||||
|
if len(line) == 0 { |
||||||
|
continue |
||||||
|
} |
||||||
|
|
||||||
|
record, err := parseGitDiffTreeLine(line) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
|
||||||
|
results = append(results, record) |
||||||
|
} |
||||||
|
|
||||||
|
if err := lines.Err(); err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
|
||||||
|
return results, nil |
||||||
|
} |
||||||
|
|
||||||
|
func parseGitDiffTreeLine(line string) (*DiffTreeRecord, error) { |
||||||
|
line = strings.TrimPrefix(line, ":") |
||||||
|
splitSections := strings.SplitN(line, "\t", 2) |
||||||
|
if len(splitSections) < 2 { |
||||||
|
return nil, fmt.Errorf("unparsable output for diff --raw: `%s`)", line) |
||||||
|
} |
||||||
|
|
||||||
|
fields := strings.Fields(splitSections[0]) |
||||||
|
if len(fields) < 5 { |
||||||
|
return nil, fmt.Errorf("unparsable output for diff --raw: `%s`, expected 5 space delimited values got %d)", line, len(fields)) |
||||||
|
} |
||||||
|
|
||||||
|
baseMode, err := git.ParseEntryMode(fields[0]) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
|
||||||
|
headMode, err := git.ParseEntryMode(fields[1]) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
|
||||||
|
baseBlobID := fields[2] |
||||||
|
headBlobID := fields[3] |
||||||
|
|
||||||
|
status, err := statusFromLetter(fields[4]) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
|
||||||
|
filePaths := strings.Split(splitSections[1], "\t") |
||||||
|
|
||||||
|
var headPath, basePath string |
||||||
|
if status == "renamed" { |
||||||
|
if len(filePaths) != 2 { |
||||||
|
return nil, fmt.Errorf("unparsable output for diff --raw: `%s`, expected 2 paths found %d", line, len(filePaths)) |
||||||
|
} |
||||||
|
basePath = filePaths[0] |
||||||
|
headPath = filePaths[1] |
||||||
|
} else { |
||||||
|
basePath = filePaths[0] |
||||||
|
headPath = filePaths[0] |
||||||
|
} |
||||||
|
|
||||||
|
return &DiffTreeRecord{ |
||||||
|
Status: status, |
||||||
|
BaseMode: baseMode, |
||||||
|
HeadMode: headMode, |
||||||
|
BaseBlobID: baseBlobID, |
||||||
|
HeadBlobID: headBlobID, |
||||||
|
BasePath: basePath, |
||||||
|
HeadPath: headPath, |
||||||
|
}, nil |
||||||
|
} |
||||||
|
|
||||||
|
func statusFromLetter(letter string) (string, error) { |
||||||
|
if len(letter) < 1 { |
||||||
|
return "", fmt.Errorf("empty status letter") |
||||||
|
} |
||||||
|
switch letter[0] { |
||||||
|
case 'A': |
||||||
|
return "added", nil |
||||||
|
case 'D': |
||||||
|
return "deleted", nil |
||||||
|
case 'M': |
||||||
|
return "modified", nil |
||||||
|
case 'R': |
||||||
|
// This is of the form "R<score>" but we are choosing to ignore the score
|
||||||
|
return "renamed", nil |
||||||
|
case 'C': |
||||||
|
// This is of the form "C<score>" but we are choosing to ignore the score
|
||||||
|
return "copied", nil |
||||||
|
case 'T': |
||||||
|
return "typechanged", nil |
||||||
|
case 'U': |
||||||
|
return "unmerged", nil |
||||||
|
case 'X': |
||||||
|
return "unknown", nil |
||||||
|
default: |
||||||
|
return "", fmt.Errorf("unknown status letter: '%s'", letter) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,424 @@ |
|||||||
|
// Copyright 2025 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package gitdiff |
||||||
|
|
||||||
|
import ( |
||||||
|
"strings" |
||||||
|
"testing" |
||||||
|
|
||||||
|
"code.gitea.io/gitea/models/db" |
||||||
|
"code.gitea.io/gitea/modules/git" |
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert" |
||||||
|
"github.com/stretchr/testify/require" |
||||||
|
) |
||||||
|
|
||||||
|
func TestGitDiffTree(t *testing.T) { |
||||||
|
test := []struct { |
||||||
|
Name string |
||||||
|
RepoPath string |
||||||
|
BaseSha string |
||||||
|
HeadSha string |
||||||
|
useMergeBase bool |
||||||
|
Expected *DiffTree |
||||||
|
}{ |
||||||
|
{ |
||||||
|
Name: "happy path", |
||||||
|
RepoPath: "./testdata/academic-module", |
||||||
|
BaseSha: "4d3d22609b895d43c2ad21096dc44a875ead8248", |
||||||
|
HeadSha: "559c156f8e0178b71cb44355428f24001b08fc68", |
||||||
|
Expected: &DiffTree{ |
||||||
|
Files: []*DiffTreeRecord{ |
||||||
|
{ |
||||||
|
Status: "modified", |
||||||
|
HeadPath: "Http/Controllers/CurriculumController.php", |
||||||
|
BasePath: "Http/Controllers/CurriculumController.php", |
||||||
|
HeadMode: git.EntryModeBlob, |
||||||
|
BaseMode: git.EntryModeBlob, |
||||||
|
HeadBlobID: "cb993acce67a5d43d40f0fd321f6be903ed945c2", |
||||||
|
BaseBlobID: "0b64a81851e374dcf25348a8f2c337e8993715a5", |
||||||
|
}, |
||||||
|
{ |
||||||
|
Status: "modified", |
||||||
|
HeadPath: "Http/Controllers/PeopleController.php", |
||||||
|
BasePath: "Http/Controllers/PeopleController.php", |
||||||
|
HeadMode: git.EntryModeBlob, |
||||||
|
BaseMode: git.EntryModeBlob, |
||||||
|
HeadBlobID: "b805a865244ca2615203f7f878fdefe69abf3054", |
||||||
|
BaseBlobID: "942504b968c56022543915e08e19781d63f03ab6", |
||||||
|
}, |
||||||
|
{ |
||||||
|
Status: "modified", |
||||||
|
HeadPath: "Http/Controllers/ProgramController.php", |
||||||
|
BasePath: "Http/Controllers/ProgramController.php", |
||||||
|
HeadMode: git.EntryModeBlob, |
||||||
|
BaseMode: git.EntryModeBlob, |
||||||
|
HeadBlobID: "b21611d5cf3e0d2af82791a8d70a2357f8517c48", |
||||||
|
BaseBlobID: "cc0c2f4f511b04f94ef6b2e08de9db8b74092d18", |
||||||
|
}, |
||||||
|
{ |
||||||
|
Status: "modified", |
||||||
|
HeadPath: "Http/Controllers/ProgramDirectorController.php", |
||||||
|
BasePath: "Http/Controllers/ProgramDirectorController.php", |
||||||
|
HeadMode: git.EntryModeBlob, |
||||||
|
BaseMode: git.EntryModeBlob, |
||||||
|
HeadBlobID: "1f41dc7d61ef1f85ad3f26814d077ea48376808a", |
||||||
|
BaseBlobID: "70eefec0be7e0aff7877ed6290acfd0ca7417c79", |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
{ |
||||||
|
Name: "first commit (no parent)", |
||||||
|
RepoPath: "./testdata/academic-module", |
||||||
|
HeadSha: "07901f79ee86272fa8935f2fe546273adaf02c89", |
||||||
|
Expected: &DiffTree{ |
||||||
|
Files: []*DiffTreeRecord{ |
||||||
|
{ |
||||||
|
Status: "added", |
||||||
|
HeadPath: "README.md", |
||||||
|
BasePath: "README.md", |
||||||
|
HeadMode: git.EntryModeBlob, |
||||||
|
BaseMode: git.EntryModeNoEntry, |
||||||
|
HeadBlobID: "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391", |
||||||
|
BaseBlobID: "0000000000000000000000000000000000000000", |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
{ |
||||||
|
Name: "first commit (no parent), merge base = true", |
||||||
|
RepoPath: "./testdata/academic-module", |
||||||
|
HeadSha: "07901f79ee86272fa8935f2fe546273adaf02c89", |
||||||
|
useMergeBase: true, |
||||||
|
Expected: &DiffTree{ |
||||||
|
Files: []*DiffTreeRecord{ |
||||||
|
{ |
||||||
|
Status: "added", |
||||||
|
HeadPath: "README.md", |
||||||
|
BasePath: "README.md", |
||||||
|
HeadMode: git.EntryModeBlob, |
||||||
|
BaseMode: git.EntryModeNoEntry, |
||||||
|
HeadBlobID: "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391", |
||||||
|
BaseBlobID: "0000000000000000000000000000000000000000", |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
{ |
||||||
|
Name: "base and head same", |
||||||
|
RepoPath: "./testdata/academic-module", |
||||||
|
BaseSha: "07901f79ee86272fa8935f2fe546273adaf02c89", |
||||||
|
HeadSha: "07901f79ee86272fa8935f2fe546273adaf02c89", |
||||||
|
Expected: &DiffTree{ |
||||||
|
Files: []*DiffTreeRecord{}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
{ |
||||||
|
Name: "file renamed", |
||||||
|
RepoPath: "./testdata/academic-module", |
||||||
|
HeadSha: "6b8722c210ee91853f77b7bb8b4b3ce706088a03", |
||||||
|
Expected: &DiffTree{ |
||||||
|
Files: []*DiffTreeRecord{ |
||||||
|
{ |
||||||
|
Status: "renamed", |
||||||
|
HeadPath: "Database/Seeders/AcademicDatabaseSeeder.php", |
||||||
|
BasePath: "Database/Seeders/AdminDatabaseSeeder.php", |
||||||
|
HeadMode: git.EntryModeBlob, |
||||||
|
BaseMode: git.EntryModeBlob, |
||||||
|
HeadBlobID: "97248f79a90aaf81fe7fd74b33c1cb182dd41783", |
||||||
|
BaseBlobID: "c8a055cfb45cd39747292983ad1797ceab40f5b1", |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
{ |
||||||
|
Name: "useMergeBase false", |
||||||
|
RepoPath: "./testdata/academic-module", |
||||||
|
BaseSha: "819dc756646ffd0730a163b5a8da65b84a6d504e", |
||||||
|
HeadSha: "528846b39d8f7e68e8081d586ea3e94be23afa7f", // this commit can be found on the update-readme branch
|
||||||
|
useMergeBase: false, |
||||||
|
Expected: &DiffTree{ |
||||||
|
Files: []*DiffTreeRecord{ |
||||||
|
{ |
||||||
|
Status: "modified", |
||||||
|
HeadPath: "README.md", |
||||||
|
BasePath: "README.md", |
||||||
|
HeadMode: git.EntryModeBlob, |
||||||
|
BaseMode: git.EntryModeBlob, |
||||||
|
HeadBlobID: "ca07ea08ae0fa243d6e0a4129843c5a25a02f499", |
||||||
|
BaseBlobID: "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391", |
||||||
|
}, |
||||||
|
{ |
||||||
|
Status: "modified", |
||||||
|
HeadPath: "webpack.mix.js", |
||||||
|
BasePath: "webpack.mix.js", |
||||||
|
HeadMode: git.EntryModeBlob, |
||||||
|
BaseMode: git.EntryModeBlob, |
||||||
|
HeadBlobID: "26825d9cb822e237b600153a26dd1e0e68457196", |
||||||
|
BaseBlobID: "344e0ca8aa791cc4164fb0ea645f334fd40d00f0", |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
{ |
||||||
|
Name: "useMergeBase true", |
||||||
|
RepoPath: "./testdata/academic-module", |
||||||
|
BaseSha: "819dc756646ffd0730a163b5a8da65b84a6d504e", |
||||||
|
HeadSha: "528846b39d8f7e68e8081d586ea3e94be23afa7f", // this commit can be found on the update-readme branch
|
||||||
|
useMergeBase: true, |
||||||
|
Expected: &DiffTree{ |
||||||
|
Files: []*DiffTreeRecord{ |
||||||
|
{ |
||||||
|
Status: "modified", |
||||||
|
HeadPath: "README.md", |
||||||
|
BasePath: "README.md", |
||||||
|
HeadMode: git.EntryModeBlob, |
||||||
|
BaseMode: git.EntryModeBlob, |
||||||
|
HeadBlobID: "ca07ea08ae0fa243d6e0a4129843c5a25a02f499", |
||||||
|
BaseBlobID: "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391", |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
{ |
||||||
|
Name: "no base set", |
||||||
|
RepoPath: "./testdata/academic-module", |
||||||
|
HeadSha: "528846b39d8f7e68e8081d586ea3e94be23afa7f", // this commit can be found on the update-readme branch
|
||||||
|
useMergeBase: false, |
||||||
|
Expected: &DiffTree{ |
||||||
|
Files: []*DiffTreeRecord{ |
||||||
|
{ |
||||||
|
Status: "modified", |
||||||
|
HeadPath: "README.md", |
||||||
|
BasePath: "README.md", |
||||||
|
HeadMode: git.EntryModeBlob, |
||||||
|
BaseMode: git.EntryModeBlob, |
||||||
|
HeadBlobID: "ca07ea08ae0fa243d6e0a4129843c5a25a02f499", |
||||||
|
BaseBlobID: "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391", |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
} |
||||||
|
|
||||||
|
for _, tt := range test { |
||||||
|
t.Run(tt.Name, func(t *testing.T) { |
||||||
|
gitRepo, err := git.OpenRepository(git.DefaultContext, tt.RepoPath) |
||||||
|
assert.NoError(t, err) |
||||||
|
defer gitRepo.Close() |
||||||
|
|
||||||
|
diffPaths, err := GetDiffTree(db.DefaultContext, gitRepo, tt.useMergeBase, tt.BaseSha, tt.HeadSha) |
||||||
|
require.NoError(t, err) |
||||||
|
|
||||||
|
assert.Equal(t, tt.Expected, diffPaths) |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func TestGitDiffTreeErrors(t *testing.T) { |
||||||
|
test := []struct { |
||||||
|
Name string |
||||||
|
RepoPath string |
||||||
|
BaseSha string |
||||||
|
HeadSha string |
||||||
|
}{ |
||||||
|
{ |
||||||
|
Name: "head doesn't exist", |
||||||
|
RepoPath: "./testdata/academic-module", |
||||||
|
BaseSha: "4d3d22609b895d43c2ad21096dc44a875ead8248", |
||||||
|
HeadSha: "asdfasdfasdf", |
||||||
|
}, |
||||||
|
{ |
||||||
|
Name: "base doesn't exist", |
||||||
|
RepoPath: "./testdata/academic-module", |
||||||
|
BaseSha: "asdfasdfasdf", |
||||||
|
HeadSha: "07901f79ee86272fa8935f2fe546273adaf02c89", |
||||||
|
}, |
||||||
|
{ |
||||||
|
Name: "head not set", |
||||||
|
RepoPath: "./testdata/academic-module", |
||||||
|
BaseSha: "07901f79ee86272fa8935f2fe546273adaf02c89", |
||||||
|
}, |
||||||
|
} |
||||||
|
|
||||||
|
for _, tt := range test { |
||||||
|
t.Run(tt.Name, func(t *testing.T) { |
||||||
|
gitRepo, err := git.OpenRepository(git.DefaultContext, tt.RepoPath) |
||||||
|
assert.NoError(t, err) |
||||||
|
defer gitRepo.Close() |
||||||
|
|
||||||
|
diffPaths, err := GetDiffTree(db.DefaultContext, gitRepo, true, tt.BaseSha, tt.HeadSha) |
||||||
|
assert.Error(t, err) |
||||||
|
assert.Nil(t, diffPaths) |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func TestParseGitDiffTree(t *testing.T) { |
||||||
|
test := []struct { |
||||||
|
Name string |
||||||
|
GitOutput string |
||||||
|
Expected []*DiffTreeRecord |
||||||
|
}{ |
||||||
|
{ |
||||||
|
Name: "file change", |
||||||
|
GitOutput: ":100644 100644 64e43d23bcd08db12563a0a4d84309cadb437e1a 5dbc7792b5bb228647cfcc8dfe65fc649119dedc M\tResources/views/curriculum/edit.blade.php", |
||||||
|
Expected: []*DiffTreeRecord{ |
||||||
|
{ |
||||||
|
Status: "modified", |
||||||
|
HeadPath: "Resources/views/curriculum/edit.blade.php", |
||||||
|
BasePath: "Resources/views/curriculum/edit.blade.php", |
||||||
|
HeadMode: git.EntryModeBlob, |
||||||
|
BaseMode: git.EntryModeBlob, |
||||||
|
HeadBlobID: "5dbc7792b5bb228647cfcc8dfe65fc649119dedc", |
||||||
|
BaseBlobID: "64e43d23bcd08db12563a0a4d84309cadb437e1a", |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
{ |
||||||
|
Name: "file added", |
||||||
|
GitOutput: ":000000 100644 0000000000000000000000000000000000000000 0063162fb403db15ceb0517b34ab782e4e58b619 A\tResources/views/class/index.blade.php", |
||||||
|
Expected: []*DiffTreeRecord{ |
||||||
|
{ |
||||||
|
Status: "added", |
||||||
|
HeadPath: "Resources/views/class/index.blade.php", |
||||||
|
BasePath: "Resources/views/class/index.blade.php", |
||||||
|
HeadMode: git.EntryModeBlob, |
||||||
|
BaseMode: git.EntryModeNoEntry, |
||||||
|
HeadBlobID: "0063162fb403db15ceb0517b34ab782e4e58b619", |
||||||
|
BaseBlobID: "0000000000000000000000000000000000000000", |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
{ |
||||||
|
Name: "file deleted", |
||||||
|
GitOutput: ":100644 000000 bac4286303c8c0017ea2f0a48c561ddcc0330a14 0000000000000000000000000000000000000000 D\tResources/views/classes/index.blade.php", |
||||||
|
Expected: []*DiffTreeRecord{ |
||||||
|
{ |
||||||
|
Status: "deleted", |
||||||
|
HeadPath: "Resources/views/classes/index.blade.php", |
||||||
|
BasePath: "Resources/views/classes/index.blade.php", |
||||||
|
HeadMode: git.EntryModeNoEntry, |
||||||
|
BaseMode: git.EntryModeBlob, |
||||||
|
HeadBlobID: "0000000000000000000000000000000000000000", |
||||||
|
BaseBlobID: "bac4286303c8c0017ea2f0a48c561ddcc0330a14", |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
{ |
||||||
|
Name: "file renamed", |
||||||
|
GitOutput: ":100644 100644 c8a055cfb45cd39747292983ad1797ceab40f5b1 97248f79a90aaf81fe7fd74b33c1cb182dd41783 R087\tDatabase/Seeders/AdminDatabaseSeeder.php\tDatabase/Seeders/AcademicDatabaseSeeder.php", |
||||||
|
Expected: []*DiffTreeRecord{ |
||||||
|
{ |
||||||
|
Status: "renamed", |
||||||
|
HeadPath: "Database/Seeders/AcademicDatabaseSeeder.php", |
||||||
|
BasePath: "Database/Seeders/AdminDatabaseSeeder.php", |
||||||
|
HeadMode: git.EntryModeBlob, |
||||||
|
BaseMode: git.EntryModeBlob, |
||||||
|
HeadBlobID: "97248f79a90aaf81fe7fd74b33c1cb182dd41783", |
||||||
|
BaseBlobID: "c8a055cfb45cd39747292983ad1797ceab40f5b1", |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
{ |
||||||
|
Name: "no changes", |
||||||
|
GitOutput: ``, |
||||||
|
Expected: []*DiffTreeRecord{}, |
||||||
|
}, |
||||||
|
{ |
||||||
|
Name: "multiple changes", |
||||||
|
GitOutput: ":000000 100644 0000000000000000000000000000000000000000 db736b44533a840981f1f17b7029d0f612b69550 A\tHttp/Controllers/ClassController.php\n" + |
||||||
|
":100644 000000 9a4d2344d4d0145db7c91b3f3e123c74367d4ef4 0000000000000000000000000000000000000000 D\tHttp/Controllers/ClassesController.php\n" + |
||||||
|
":100644 100644 f060d6aede65d423f49e7dc248dfa0d8835ef920 b82c8e39a3602dedadb44669956d6eb5b6a7cc86 M\tHttp/Controllers/ProgramDirectorController.php\n", |
||||||
|
Expected: []*DiffTreeRecord{ |
||||||
|
{ |
||||||
|
Status: "added", |
||||||
|
HeadPath: "Http/Controllers/ClassController.php", |
||||||
|
BasePath: "Http/Controllers/ClassController.php", |
||||||
|
HeadMode: git.EntryModeBlob, |
||||||
|
BaseMode: git.EntryModeNoEntry, |
||||||
|
HeadBlobID: "db736b44533a840981f1f17b7029d0f612b69550", |
||||||
|
BaseBlobID: "0000000000000000000000000000000000000000", |
||||||
|
}, |
||||||
|
{ |
||||||
|
Status: "deleted", |
||||||
|
HeadPath: "Http/Controllers/ClassesController.php", |
||||||
|
BasePath: "Http/Controllers/ClassesController.php", |
||||||
|
HeadMode: git.EntryModeNoEntry, |
||||||
|
BaseMode: git.EntryModeBlob, |
||||||
|
HeadBlobID: "0000000000000000000000000000000000000000", |
||||||
|
BaseBlobID: "9a4d2344d4d0145db7c91b3f3e123c74367d4ef4", |
||||||
|
}, |
||||||
|
{ |
||||||
|
Status: "modified", |
||||||
|
HeadPath: "Http/Controllers/ProgramDirectorController.php", |
||||||
|
BasePath: "Http/Controllers/ProgramDirectorController.php", |
||||||
|
HeadMode: git.EntryModeBlob, |
||||||
|
BaseMode: git.EntryModeBlob, |
||||||
|
HeadBlobID: "b82c8e39a3602dedadb44669956d6eb5b6a7cc86", |
||||||
|
BaseBlobID: "f060d6aede65d423f49e7dc248dfa0d8835ef920", |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
{ |
||||||
|
Name: "spaces in file path", |
||||||
|
GitOutput: ":000000 100644 0000000000000000000000000000000000000000 db736b44533a840981f1f17b7029d0f612b69550 A\tHttp /Controllers/Class Controller.php\n" + |
||||||
|
":100644 000000 9a4d2344d4d0145db7c91b3f3e123c74367d4ef4 0000000000000000000000000000000000000000 D\tHttp/Cont rollers/Classes Controller.php\n" + |
||||||
|
":100644 100644 f060d6aede65d423f49e7dc248dfa0d8835ef920 b82c8e39a3602dedadb44669956d6eb5b6a7cc86 R\tHttp/Controllers/Program Director Controller.php\tHttp/Cont rollers/ProgramDirectorController.php\n", |
||||||
|
Expected: []*DiffTreeRecord{ |
||||||
|
{ |
||||||
|
Status: "added", |
||||||
|
HeadPath: "Http /Controllers/Class Controller.php", |
||||||
|
BasePath: "Http /Controllers/Class Controller.php", |
||||||
|
HeadMode: git.EntryModeBlob, |
||||||
|
BaseMode: git.EntryModeNoEntry, |
||||||
|
HeadBlobID: "db736b44533a840981f1f17b7029d0f612b69550", |
||||||
|
BaseBlobID: "0000000000000000000000000000000000000000", |
||||||
|
}, |
||||||
|
{ |
||||||
|
Status: "deleted", |
||||||
|
HeadPath: "Http/Cont rollers/Classes Controller.php", |
||||||
|
BasePath: "Http/Cont rollers/Classes Controller.php", |
||||||
|
HeadMode: git.EntryModeNoEntry, |
||||||
|
BaseMode: git.EntryModeBlob, |
||||||
|
HeadBlobID: "0000000000000000000000000000000000000000", |
||||||
|
BaseBlobID: "9a4d2344d4d0145db7c91b3f3e123c74367d4ef4", |
||||||
|
}, |
||||||
|
{ |
||||||
|
Status: "renamed", |
||||||
|
HeadPath: "Http/Cont rollers/ProgramDirectorController.php", |
||||||
|
BasePath: "Http/Controllers/Program Director Controller.php", |
||||||
|
HeadMode: git.EntryModeBlob, |
||||||
|
BaseMode: git.EntryModeBlob, |
||||||
|
HeadBlobID: "b82c8e39a3602dedadb44669956d6eb5b6a7cc86", |
||||||
|
BaseBlobID: "f060d6aede65d423f49e7dc248dfa0d8835ef920", |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
{ |
||||||
|
Name: "file type changed", |
||||||
|
GitOutput: ":100644 120000 344e0ca8aa791cc4164fb0ea645f334fd40d00f0 a7c2973de00bfdc6ca51d315f401b5199fe01dc3 T\twebpack.mix.js", |
||||||
|
Expected: []*DiffTreeRecord{ |
||||||
|
{ |
||||||
|
Status: "typechanged", |
||||||
|
HeadPath: "webpack.mix.js", |
||||||
|
BasePath: "webpack.mix.js", |
||||||
|
HeadMode: git.EntryModeSymlink, |
||||||
|
BaseMode: git.EntryModeBlob, |
||||||
|
HeadBlobID: "a7c2973de00bfdc6ca51d315f401b5199fe01dc3", |
||||||
|
BaseBlobID: "344e0ca8aa791cc4164fb0ea645f334fd40d00f0", |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
} |
||||||
|
|
||||||
|
for _, tt := range test { |
||||||
|
t.Run(tt.Name, func(t *testing.T) { |
||||||
|
entries, err := parseGitDiffTree(strings.NewReader(tt.GitOutput)) |
||||||
|
assert.NoError(t, err) |
||||||
|
assert.Equal(t, tt.Expected, entries) |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
@ -1 +1,2 @@ |
|||||||
0000000000000000000000000000000000000000 bd7063cc7c04689c4d082183d32a604ed27a24f9 Lunny Xiao <xiaolunwen@gmail.com> 1574829684 +0800 clone: from https://try.gitea.io/shemgp-aiias/academic-module |
0000000000000000000000000000000000000000 bd7063cc7c04689c4d082183d32a604ed27a24f9 Lunny Xiao <xiaolunwen@gmail.com> 1574829684 +0800 clone: from https://try.gitea.io/shemgp-aiias/academic-module |
||||||
|
0000000000000000000000000000000000000000 819dc756646ffd0730a163b5a8da65b84a6d504e Alexander McRae <alex@allspice.io> 1737998543 -0800 push |
||||||
|
@ -1 +1,2 @@ |
|||||||
0000000000000000000000000000000000000000 bd7063cc7c04689c4d082183d32a604ed27a24f9 Lunny Xiao <xiaolunwen@gmail.com> 1574829684 +0800 clone: from https://try.gitea.io/shemgp-aiias/academic-module |
0000000000000000000000000000000000000000 bd7063cc7c04689c4d082183d32a604ed27a24f9 Lunny Xiao <xiaolunwen@gmail.com> 1574829684 +0800 clone: from https://try.gitea.io/shemgp-aiias/academic-module |
||||||
|
bd7063cc7c04689c4d082183d32a604ed27a24f9 819dc756646ffd0730a163b5a8da65b84a6d504e Alexander McRae <alex@allspice.io> 1737998543 -0800 push |
||||||
|
@ -0,0 +1 @@ |
|||||||
|
0000000000000000000000000000000000000000 528846b39d8f7e68e8081d586ea3e94be23afa7f Alexander McRae <alex@allspice.io> 1737998161 -0800 push |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1 +1 @@ |
|||||||
bd7063cc7c04689c4d082183d32a604ed27a24f9 |
819dc756646ffd0730a163b5a8da65b84a6d504e |
||||||
|
@ -0,0 +1 @@ |
|||||||
|
528846b39d8f7e68e8081d586ea3e94be23afa7f |
Loading…
Reference in new issue