sigs.k8s.io/release-sdk@v0.11.1-0.20240417074027-8061fb5e4952/git/git_test.go (about)

     1  /*
     2  Copyright 2020 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package git_test
    18  
    19  import (
    20  	"errors"
    21  	"fmt"
    22  	"os"
    23  	"os/exec"
    24  	"path/filepath"
    25  	"strconv"
    26  	"strings"
    27  	"testing"
    28  	"time"
    29  
    30  	gogit "github.com/go-git/go-git/v5"
    31  	"github.com/go-git/go-git/v5/plumbing/object"
    32  	"github.com/stretchr/testify/require"
    33  
    34  	"sigs.k8s.io/release-sdk/git"
    35  	"sigs.k8s.io/release-sdk/git/gitfakes"
    36  	"sigs.k8s.io/release-utils/command"
    37  )
    38  
    39  var testAuthor = &object.Signature{
    40  	Name:  "John Doe",
    41  	Email: "john@doe.org",
    42  	When:  time.Now(),
    43  }
    44  
    45  func newSUT() (*git.Repo, *gitfakes.FakeWorktree) {
    46  	repoMock := &gitfakes.FakeRepository{}
    47  	worktreeMock := &gitfakes.FakeWorktree{}
    48  
    49  	repo := &git.Repo{}
    50  	repo.SetWorktree(worktreeMock)
    51  	repo.SetInnerRepo(repoMock)
    52  
    53  	return repo, worktreeMock
    54  }
    55  
    56  func TestCommit(t *testing.T) {
    57  	repo, worktreeMock := newSUT()
    58  	require.Nil(t, repo.Commit("msg"))
    59  	require.Equal(t, worktreeMock.CommitCallCount(), 1)
    60  }
    61  
    62  func TestGetDefaultKubernetesRepoURLSuccess(t *testing.T) {
    63  	testcases := []struct {
    64  		name     string
    65  		org      string
    66  		useSSH   bool
    67  		expected string
    68  	}{
    69  		{
    70  			name:     "default HTTPS",
    71  			expected: "https://github.com/kubernetes/kubernetes",
    72  		},
    73  	}
    74  
    75  	for _, tc := range testcases {
    76  		t.Logf("Test case: %s", tc.name)
    77  
    78  		actual := git.GetDefaultKubernetesRepoURL()
    79  		require.Equal(t, tc.expected, actual)
    80  	}
    81  }
    82  
    83  // createTestRepository creates a test repo, cd into it and returns the path
    84  func createTestRepository() (repoPath string, err error) {
    85  	repoPath, err = os.MkdirTemp("", "sigrelease-test-repo-*")
    86  	if err != nil {
    87  		return "", fmt.Errorf("creating a directory for test repository: %w", err)
    88  	}
    89  	if err := os.Chdir(repoPath); err != nil {
    90  		return "", fmt.Errorf("cd'ing into test repository: %w", err)
    91  	}
    92  	out, err := exec.Command("git", "init").Output()
    93  	if err != nil {
    94  		return "", fmt.Errorf("initializing test repository: %s: %w", out, err)
    95  	}
    96  	return repoPath, nil
    97  }
    98  
    99  func TestGetUserName(t *testing.T) {
   100  	const fakeUserName = "SIG Release Test User"
   101  	currentDir, err := os.Getwd()
   102  	require.Nil(t, err, "error reading the current directory")
   103  	defer os.Chdir(currentDir) //nolint: errcheck
   104  
   105  	// Create an empty repo and configure the users name to test
   106  	repoPath, err := createTestRepository()
   107  	require.Nil(t, err, "getting a test repo")
   108  
   109  	// Call git to configure the user's name:
   110  	_, err = exec.Command("git", "config", "user.name", fakeUserName).Output()
   111  	require.Nil(t, err, fmt.Sprintf("configuring fake user email in %s", repoPath))
   112  
   113  	testRepo, err := git.OpenRepo(repoPath)
   114  	require.Nil(t, err, fmt.Sprintf("opening test repo in %s", repoPath))
   115  	defer testRepo.Cleanup() //nolint: errcheck
   116  
   117  	actual, err := git.GetUserName()
   118  	require.Nil(t, err)
   119  	require.Equal(t, fakeUserName, actual)
   120  	require.NotEqual(t, fakeUserName, "")
   121  }
   122  
   123  func TestGetUserEmail(t *testing.T) {
   124  	const fakeUserEmail = "kubernetes-test@example.com"
   125  	currentDir, err := os.Getwd()
   126  	require.Nil(t, err, "error reading the current directory")
   127  	defer os.Chdir(currentDir) //nolint: errcheck
   128  
   129  	// Create an empty repo and configure the users name to test
   130  	repoPath, err := createTestRepository()
   131  	require.Nil(t, err, "getting a test repo")
   132  
   133  	// Call git to configure the user's name:
   134  	_, err = exec.Command("git", "config", "user.email", fakeUserEmail).Output()
   135  	require.Nil(t, err, fmt.Sprintf("configuring fake user email in %s", repoPath))
   136  
   137  	testRepo, err := git.OpenRepo(repoPath)
   138  	require.Nil(t, err, fmt.Sprintf("opening test repo in %s", repoPath))
   139  	defer testRepo.Cleanup() //nolint: errcheck
   140  
   141  	// Do the actual call
   142  	actual, err := git.GetUserEmail()
   143  	require.Nil(t, err)
   144  	require.Equal(t, fakeUserEmail, actual)
   145  	require.NotEqual(t, fakeUserEmail, "")
   146  }
   147  
   148  func TestGetKubernetesRepoURLSuccess(t *testing.T) {
   149  	testcases := []struct {
   150  		name     string
   151  		org      string
   152  		useSSH   bool
   153  		expected string
   154  	}{
   155  		{
   156  			name:     "default HTTPS",
   157  			expected: "https://github.com/kubernetes/kubernetes",
   158  		},
   159  		{
   160  			name:     "ssh with custom org",
   161  			org:      "fake-org",
   162  			useSSH:   true,
   163  			expected: "git@github.com:fake-org/kubernetes",
   164  		},
   165  	}
   166  
   167  	for _, tc := range testcases {
   168  		t.Logf("Test case: %s", tc.name)
   169  
   170  		actual := git.GetKubernetesRepoURL(tc.org, tc.useSSH)
   171  		require.Equal(t, tc.expected, actual)
   172  	}
   173  }
   174  
   175  func TestGetRepoURLSuccess(t *testing.T) {
   176  	testcases := []struct {
   177  		name     string
   178  		org      string
   179  		repo     string
   180  		useSSH   bool
   181  		expected string
   182  	}{
   183  		{
   184  			name:     "default Kubernetes HTTPS",
   185  			org:      "kubernetes",
   186  			repo:     "kubernetes",
   187  			expected: "https://github.com/kubernetes/kubernetes",
   188  		},
   189  		{
   190  			name:     "ssh with custom org",
   191  			org:      "fake-org",
   192  			repo:     "repofoo",
   193  			useSSH:   true,
   194  			expected: "git@github.com:fake-org/repofoo",
   195  		},
   196  	}
   197  
   198  	for _, tc := range testcases {
   199  		t.Logf("Test case: %s", tc.name)
   200  
   201  		actual := git.GetRepoURL(tc.org, tc.repo, tc.useSSH)
   202  		require.Equal(t, tc.expected, actual)
   203  	}
   204  }
   205  
   206  func TestRemotify(t *testing.T) {
   207  	testcases := []struct{ provided, expected string }{
   208  		{provided: git.DefaultBranch, expected: git.DefaultRemote + "/" + git.DefaultBranch},
   209  		{provided: "origin/ref", expected: "origin/ref"},
   210  		{provided: "base/another_ref", expected: "base/another_ref"},
   211  	}
   212  
   213  	for _, tc := range testcases {
   214  		require.Equal(t, git.Remotify(tc.provided), tc.expected)
   215  	}
   216  }
   217  
   218  func TestIsDirtyMockSuccess(t *testing.T) {
   219  	repo, _ := newSUT()
   220  
   221  	dirty, err := repo.IsDirty()
   222  
   223  	require.Nil(t, err)
   224  	require.False(t, dirty)
   225  }
   226  
   227  func TestIsDirtyMockSuccessDirty(t *testing.T) {
   228  	repo, worktreeMock := newSUT()
   229  	worktreeMock.StatusReturns(gogit.Status{
   230  		"file": &gogit.FileStatus{
   231  			Worktree: gogit.Modified,
   232  		},
   233  	}, nil)
   234  
   235  	dirty, err := repo.IsDirty()
   236  
   237  	require.Nil(t, err)
   238  	require.True(t, dirty)
   239  }
   240  
   241  func TestIsDirtyMockFailureWorktreeStatus(t *testing.T) {
   242  	repo, worktreeMock := newSUT()
   243  	worktreeMock.StatusReturns(gogit.Status{}, errors.New(""))
   244  
   245  	dirty, err := repo.IsDirty()
   246  
   247  	require.NotNil(t, err)
   248  	require.False(t, dirty)
   249  }
   250  
   251  func TestParseRepoSlug(t *testing.T) {
   252  	slugTests := []struct {
   253  		caseName, repoSlug, orgName, repoName string
   254  		isValid                               bool
   255  	}{
   256  		{
   257  			caseName: "valid slug", repoSlug: "kubernetes/release",
   258  			orgName: "kubernetes", repoName: "release", isValid: true,
   259  		},
   260  
   261  		{
   262  			caseName: "slug with hyphens", repoSlug: "kubernetes/repo_with_underscores",
   263  			orgName: "", repoName: "", isValid: false,
   264  		},
   265  
   266  		{
   267  			caseName: "slug with dashes", repoSlug: "kubernetes-sigs/release-notes",
   268  			orgName: "kubernetes-sigs", repoName: "release-notes", isValid: true,
   269  		},
   270  
   271  		{
   272  			caseName: "slug with uppercase", repoSlug: "GoogleCloudPlatform/compute-image-tools",
   273  			orgName: "GoogleCloudPlatform", repoName: "compute-image-tools", isValid: true,
   274  		},
   275  
   276  		{
   277  			caseName: "slug with invalid chars", repoSlug: "kubern#etes/not.valid",
   278  			orgName: "", repoName: "", isValid: false,
   279  		},
   280  
   281  		{
   282  			caseName: "slug with extra slash", repoSlug: "kubernetes/not/valid",
   283  			orgName: "", repoName: "", isValid: false,
   284  		},
   285  
   286  		{
   287  			caseName: "slug with only org", repoSlug: "kubernetes",
   288  			orgName: "kubernetes", repoName: "", isValid: true,
   289  		},
   290  	}
   291  
   292  	for _, testCase := range slugTests {
   293  		org, repo, err := git.ParseRepoSlug(testCase.repoSlug)
   294  		if testCase.isValid {
   295  			require.Nil(t, err, testCase.caseName)
   296  		} else {
   297  			require.NotNil(t, err, testCase.caseName)
   298  		}
   299  		require.Equal(t, testCase.orgName, org, testCase.caseName)
   300  		require.Equal(t, testCase.repoName, repo, testCase.caseName)
   301  	}
   302  }
   303  
   304  func TestRetryErrors(t *testing.T) {
   305  	retryErrorStrings := []string{
   306  		"dial tcp: lookup github.com on [::1]:53",
   307  		"read udp [::1]:48087->[::1]:53",
   308  		"read: connection refused",
   309  	}
   310  
   311  	nonRetryErrorStrings := []string{
   312  		"could not list references on the remote repository",
   313  		"error checking remote branch",
   314  		"src refspec release-chorizo does not match",
   315  	}
   316  
   317  	for _, message := range retryErrorStrings {
   318  		err := git.NewNetworkError(errors.New(message))
   319  		require.True(t, err.CanRetry(), fmt.Sprintf("Checking retriable error '%s'", message))
   320  	}
   321  
   322  	for _, message := range nonRetryErrorStrings {
   323  		err := git.NewNetworkError(errors.New(message))
   324  		require.False(t, err.CanRetry(), fmt.Sprintf("Checking non-retriable error '%s'", message))
   325  	}
   326  }
   327  
   328  func TestNetworkError(t *testing.T) {
   329  	// Return a NetWorkError in a fun that returns a standard error
   330  	err := func() error {
   331  		return git.NewNetworkError(errors.New("This is a test error"))
   332  	}()
   333  	require.NotNil(t, err, "checking if NewNetWork error returns nil")
   334  	require.NotEmpty(t, err.Error(), "checking if NetworkError returns a message")
   335  	require.False(t, err.(git.NetworkError).CanRetry(), "checking if network error can be properly asserted")
   336  }
   337  
   338  func TestHasBranch(t *testing.T) {
   339  	testBranchName := "git-package-test-branch"
   340  	repoPath, err := createTestRepository()
   341  	require.Nil(t, err, "getting a test repo")
   342  
   343  	// Create a file and a test commit
   344  	testfile := filepath.Join(repoPath, "README.md")
   345  	err = os.WriteFile(testfile, []byte("# WHY SIG-RELEASE ROCKS\n\n"), os.FileMode(0o644))
   346  	require.Nil(t, err, "writing test file")
   347  
   348  	err = command.NewWithWorkDir(repoPath, "git", "add", testfile).RunSuccess()
   349  	require.Nil(t, err, fmt.Sprintf("adding test file in %s", repoPath))
   350  
   351  	err = command.NewWithWorkDir(repoPath, "git", "commit", "-m", "adding test file").RunSuccess()
   352  	require.Nil(t, err, "creating first commit")
   353  
   354  	// Call git to configure the user's name:
   355  	err = command.NewWithWorkDir(repoPath, "git", "branch", testBranchName).RunSuccess()
   356  	require.Nil(t, err, fmt.Sprintf("configuring test branch in %s", repoPath))
   357  
   358  	// Now, open the repo and test to see if branches are there
   359  	testRepo, err := git.OpenRepo(repoPath)
   360  	require.Nil(t, err, fmt.Sprintf("opening test repo in %s", repoPath))
   361  	defer testRepo.Cleanup() //nolint: errcheck
   362  
   363  	actual, err := testRepo.HasBranch(testBranchName)
   364  	require.Nil(t, err)
   365  	require.True(t, actual)
   366  
   367  	actual, err = testRepo.HasBranch(git.DefaultBranch)
   368  	require.Nil(t, err)
   369  	require.True(t, actual)
   370  
   371  	actual, err = testRepo.HasBranch("non-existing-branch")
   372  	require.Nil(t, err)
   373  	require.False(t, actual)
   374  }
   375  
   376  func TestStatus(t *testing.T) {
   377  	rawRepoDir, err := os.MkdirTemp("", "k8s-test-repo")
   378  	require.Nil(t, err)
   379  	_, err = gogit.PlainInit(rawRepoDir, false)
   380  	require.Nil(t, err)
   381  
   382  	testFile := "test-status.txt"
   383  
   384  	testRepo, err := git.OpenRepo(rawRepoDir)
   385  	require.Nil(t, err)
   386  	defer testRepo.Cleanup() //nolint: errcheck
   387  
   388  	// Get the status object
   389  	status, err := testRepo.Status()
   390  	require.Nil(t, err)
   391  	require.NotNil(t, status)
   392  	require.True(t, status.IsClean())
   393  
   394  	// Create an untracked file
   395  	require.Nil(t, os.WriteFile(filepath.Join(testRepo.Dir(), testFile), []byte("Hello SIG Release"), 0o644))
   396  
   397  	// Status should be modified now
   398  	status, err = testRepo.Status()
   399  	require.Nil(t, err)
   400  	require.Equal(t, fmt.Sprintf("?? %s\n", testFile), status.String())
   401  
   402  	// Add the file, should status should be A
   403  	require.Nil(t, testRepo.Add(testFile))
   404  	status, err = testRepo.Status()
   405  	require.Nil(t, err)
   406  	require.Equal(t, fmt.Sprintf("A  %s\n", testFile), status.String())
   407  
   408  	// Commit the file, status should be blank again
   409  	require.Nil(t, testRepo.Commit("Commit test file"))
   410  	status, err = testRepo.Status()
   411  	require.Nil(t, err)
   412  	require.Empty(t, status.String())
   413  
   414  	// Modify the file
   415  	require.Nil(t, os.WriteFile(filepath.Join(testRepo.Dir(), testFile), []byte("Bye SIG Release"), 0o644))
   416  	status, err = testRepo.Status()
   417  	require.Nil(t, err)
   418  	require.Equal(t, fmt.Sprintf(" M %s\n", testFile), status.String())
   419  }
   420  
   421  func TestShowLastCommit(t *testing.T) {
   422  	rawRepoDir, err := os.MkdirTemp("", "k8s-test-repo")
   423  	require.Nil(t, err)
   424  	_, err = gogit.PlainInit(rawRepoDir, false)
   425  	require.Nil(t, err)
   426  
   427  	testFile := "test-last-commit.txt"
   428  	timeNow := strconv.FormatInt(time.Now().UnixNano(), 10)
   429  
   430  	testRepo, err := git.OpenRepo(rawRepoDir)
   431  	require.Nil(t, err)
   432  	defer testRepo.Cleanup() //nolint: errcheck
   433  
   434  	// Create an untracked file
   435  	require.Nil(t, os.WriteFile(filepath.Join(testRepo.Dir(), testFile), []byte("Hello SIG Release"), 0o644))
   436  	require.Nil(t, testRepo.Add(testFile))
   437  	require.Nil(t, testRepo.Commit(fmt.Sprintf("Commit test file at %s", timeNow)))
   438  
   439  	// Now get the log message back and check if it contains the time
   440  	lastLog, err := testRepo.ShowLastCommit()
   441  	require.Nil(t, err)
   442  	require.NotEmpty(t, lastLog)
   443  	require.True(t, strings.Contains(lastLog, timeNow))
   444  }
   445  
   446  func TestFetchRemote(t *testing.T) {
   447  	testTagName := "test-tag" + strconv.FormatInt(time.Now().UnixNano(), 10)
   448  	// Create a new empty repo
   449  	rawRepoDir, err := os.MkdirTemp("", "k8s-test-repo")
   450  	require.Nil(t, err)
   451  	gogitRepo, err := gogit.PlainInit(rawRepoDir, false)
   452  	require.Nil(t, err)
   453  
   454  	// Create the foirst commit
   455  	wtree, err := gogitRepo.Worktree()
   456  	require.Nil(t, err)
   457  	require.Nil(t, err)
   458  	commitSha, err := wtree.Commit("Initial Commit", &gogit.CommitOptions{
   459  		Author:            testAuthor,
   460  		AllowEmptyCommits: true,
   461  	})
   462  	require.Nil(t, err)
   463  
   464  	// Create a git.Repo from it
   465  	originRepo, err := git.OpenRepo(rawRepoDir)
   466  	require.Nil(t, err)
   467  
   468  	branchName, err := originRepo.CurrentBranch()
   469  	require.Nil(t, err)
   470  	defer originRepo.Cleanup() //nolint: errcheck
   471  
   472  	// Create a new clone of the original repo
   473  	testRepo, err := git.CloneOrOpenRepo("", rawRepoDir, false, true, nil)
   474  	require.Nil(t, err)
   475  	defer testRepo.Cleanup() //nolint: errcheck
   476  
   477  	// The initial clone must not have any tags
   478  	testTags, err := testRepo.TagsForBranch(branchName)
   479  	require.Nil(t, err)
   480  	require.Empty(t, testTags)
   481  
   482  	// Create a tag on the originRepo
   483  	_, err = gogitRepo.CreateTag(testTagName, commitSha, &gogit.CreateTagOptions{
   484  		Message: testTagName,
   485  		Tagger:  testAuthor,
   486  	})
   487  	require.Nil(t, err)
   488  
   489  	// Now, call fetch
   490  	newContent, err := testRepo.FetchRemote("origin")
   491  	require.Nil(t, err, "Calling fetch to get a test tag")
   492  	require.True(t, newContent)
   493  
   494  	// Fetching again should provide no updates
   495  	newContent, err = testRepo.FetchRemote("origin")
   496  	require.Nil(t, err, "Calling fetch to get a test tag again")
   497  	require.False(t, newContent)
   498  
   499  	// And now we can verify the tags was successfully transferred via FetchRemote()
   500  	testTags, err = testRepo.TagsForBranch(branchName)
   501  	require.Nil(t, err)
   502  	require.NotEmpty(t, testTags)
   503  	require.ElementsMatch(t, []string{testTagName}, testTags)
   504  }
   505  
   506  func TestRebase(t *testing.T) {
   507  	testFile := "test-rebase.txt"
   508  
   509  	// Create a new empty repo
   510  	rawRepoDir, err := os.MkdirTemp("", "k8s-test-repo")
   511  	require.Nil(t, err)
   512  	gogitRepo, err := gogit.PlainInit(rawRepoDir, false)
   513  	require.Nil(t, err)
   514  
   515  	// Create the initial commit
   516  	wtree, err := gogitRepo.Worktree()
   517  	require.Nil(t, err)
   518  	_, err = wtree.Commit("Initial Commit", &gogit.CommitOptions{
   519  		Author:            testAuthor,
   520  		AllowEmptyCommits: true,
   521  	})
   522  	require.Nil(t, err)
   523  
   524  	// Create a git.Repo from it
   525  	originRepo, err := git.OpenRepo(rawRepoDir)
   526  	require.Nil(t, err)
   527  
   528  	branchName, err := originRepo.CurrentBranch()
   529  	require.Nil(t, err)
   530  	defer originRepo.Cleanup() //nolint: errcheck
   531  
   532  	// Create a new clone of the original repo
   533  	testRepo, err := git.CloneOrOpenRepo("", rawRepoDir, false, true, nil)
   534  	require.Nil(t, err)
   535  	defer testRepo.Cleanup() //nolint: errcheck
   536  
   537  	// Test 1. Rebase should not fail if both repos are in sync
   538  	require.Nil(t, testRepo.Rebase(fmt.Sprintf("origin/%s", branchName)), "cloning synchronizaed repos")
   539  
   540  	// Test 2. Rebase should not fail with pulling changes in the remote
   541  	require.Nil(t, os.WriteFile(filepath.Join(rawRepoDir, testFile), []byte("Hello SIG Release"), 0o644))
   542  	_, err = wtree.Add(testFile)
   543  	require.Nil(t, err)
   544  
   545  	_, err = wtree.Commit("Test2-Commit", &gogit.CommitOptions{
   546  		Author: testAuthor,
   547  	})
   548  	require.Nil(t, err)
   549  
   550  	// Pull the changes to the test repo
   551  	newContent, err := testRepo.FetchRemote("origin")
   552  	require.Nil(t, err)
   553  	require.True(t, newContent)
   554  
   555  	// Do the Rebase
   556  	require.Nil(t, testRepo.Rebase(fmt.Sprintf("origin/%s", branchName)), "rebasing changes from origin")
   557  
   558  	// Verify we got the commit
   559  	lastLog, err := testRepo.ShowLastCommit()
   560  	require.Nil(t, err)
   561  	require.True(t, strings.Contains(lastLog, "Test2-Commit"))
   562  
   563  	// Test 3: Rebase must on an invalid branch
   564  	require.NotNil(t, testRepo.Rebase("origin/invalidBranch"), "rebasing to invalid branch")
   565  
   566  	// Test 4: Rebase must fail on merge conflicts
   567  	require.Nil(t, os.WriteFile(filepath.Join(rawRepoDir, testFile), []byte("Hello again SIG Release"), 0o644))
   568  	_, err = wtree.Add(testFile)
   569  	require.Nil(t, err)
   570  
   571  	_, err = wtree.Commit("Test4-Commit", &gogit.CommitOptions{
   572  		Author: testAuthor,
   573  	})
   574  	require.Nil(t, err)
   575  
   576  	// Commit the same file in the test repo
   577  	require.Nil(t, os.WriteFile(filepath.Join(testRepo.Dir(), testFile), []byte("Conflict me!"), 0o644))
   578  	require.Nil(t, testRepo.Add(filepath.Join(testRepo.Dir(), testFile)))
   579  	require.Nil(t, testRepo.Commit("Adding file to cause conflict"))
   580  
   581  	// Now, fetch and rebase
   582  	newContent, err = testRepo.FetchRemote("origin")
   583  	require.Nil(t, err)
   584  	require.True(t, newContent)
   585  
   586  	err = testRepo.Rebase(fmt.Sprintf("origin/%s", branchName))
   587  	require.NotNil(t, err, "testing for merge conflicts")
   588  }
   589  
   590  func TestLastCommitSha(t *testing.T) {
   591  	// Create a test repository
   592  	rawRepoDir, err := os.MkdirTemp("", "k8s-test-repo")
   593  	require.Nil(t, err)
   594  	defer os.RemoveAll(rawRepoDir)
   595  	_, err = gogit.PlainInit(rawRepoDir, false)
   596  	require.Nil(t, err)
   597  
   598  	repo, err := git.OpenRepo(rawRepoDir)
   599  	require.Nil(t, err)
   600  
   601  	// Create two commits in the repository
   602  	shas := make([]string, 2)
   603  	for _, i := range []int{0, 1} {
   604  		require.Nil(t, repo.CommitEmpty(fmt.Sprintf("Empty commit %d", i+1)))
   605  		shas[i], err = repo.LastCommitSha()
   606  		require.Nil(t, err)
   607  		require.NotEmpty(t, shas[i])
   608  	}
   609  	require.Len(t, shas, 2)
   610  
   611  	// Now, checkout the first one and check we get the right hash
   612  	require.Nil(t, repo.Checkout("HEAD~1"))
   613  
   614  	lastCommit, err := repo.LastCommitSha()
   615  	require.Nil(t, err)
   616  	require.Equal(t, shas[0], lastCommit, "Checking HEAD~1 sha matches commit #1")
   617  	require.NotEqual(t, shas[1], lastCommit, "Checking HEAD~1 sha does not matches commit #2")
   618  }