RemoveReference should clean up empty directories

Open Beginner friendly
#1,822 3 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
68/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Quiet
Tech stack
go
Domain
backend

Research direction

Start with the filesystem storage implementations of SetReference and RemoveReference, then use the provided TestConflictWithDeletedRef reproduction as the first test to run. Done means removing a nested reference also removes empty parent directories, allowing a later reference named bugfix to be created successfully.

Written by the indexing model from the issue text.

Description

bug good first issue help wanted
Bug Description

When SetReference is called to create a reference with / in the name, it creates subdirectories for each part of the name separated by a slash. For example, when creating the ref bugfix/issue-1 SetReference would create it at the path <repo>/refs/bugfix/issue-1, creating the file issue-1 and the parent directory bugfix/.

When RemoveReference is called on the same reference, it deletes the reference but not the parent directories, even though they may be empty. In the example above, it would delete issue-1, but the refs/heads/bugfix/ directory would still exist.

Since the parent directories are not cleaned up, it becomes impossible to create any new references that share the same name. In the example above, it is now not possible to use SetReference to create a ref with the name bugfix, since it conflicts with the leftover directory refs/heads/bugfix/.

The Git implementation does cleanup parent directories when a branch is deleted.

go-git Version

v5.16.4

Steps to Reproduce
func TestConflictWithDeletedRef(t *testing.T) {
	repoPath := t.TempDir()
	fs := filesystem.NewStorage(osfs.New(repoPath), cache.NewObjectLRUDefault())
	if _, err := git.Init(fs, nil); err != nil {
		t.Fatalf("Failed to init git: %v", err)
	}

	// Create a new branch named bugfix/1.
	nestedRef := plumbing.NewReferenceFromStrings("refs/heads/bugfix/1", "target1")
	if err := fs.SetReference(nestedRef); err != nil {
		t.Fatalf("Failed to set reference: %v", err)
	}

	if err := fs.RemoveReference(nestedRef.Name()); err != nil {
		t.Fatalf("Failed to remove reference: %v", err)
	}

	// Now we try to create a ref named 'bugfix' but it fails, since the directory refs/heads/bugfix/ exists.
	// Error:
	// Failed to set reference: open /tmp/TestConflictWithDeletedRef4236784409/001/refs/heads/bugfix: is a directory
	conflictingRef := plumbing.NewReferenceFromStrings("refs/heads/bugfix", "target2")
	if err := fs.SetReference(conflictingRef); err != nil {
		t.Fatalf("Failed to set reference: %v", err)
	}
}
Additional Information

No response

Dominant language
Go
Stars
7.7k
Forks
1k
Avg merge
1d 1h
Merged PRs (30d)
21

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from go-git/go-git

All issues in go-git/go-git

Similar issues

More Go issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.