go-git icon indicating copy to clipboard operation
go-git copied to clipboard

Question: diff --name-only command implementation

Open nerro opened this issue 5 years ago • 10 comments

Hey-hey, I tried to create following git command with go-git: git diff --name-only --diff-filter=AM HEAD~5 HEAD -- . At some point in my code I have a Patch and iterate over stats:

patch, _ := lastCommit.Patch(&firstCommit)
for i := 0; i < len(patch.Stats()); i++ {
	changedFiles = append(changedFiles, patch.Stats()[i].Name)
}

but all binaries files are missing. I guess because of https://github.com/src-d/go-git/blob/3bd5e82b2512d85becae9677fa06b5a973fd4cfb/plumbing/object/patch.go#L302

Perhaps I don't need Patch object and can do it differently? Thanks for pointing me in the "right" direction.

nerro avatar Nov 01 '18 22:11 nerro

@nerro Do you still have an example of your code?

StevenACoffman avatar Nov 17 '19 22:11 StevenACoffman

Do you still have your code? I assume something like this:

		currentTree, err := commit.Tree()
		if err != nil {
			return err
		}

		prevTree, err := prevCommit.Tree()
		if err != nil {
			return err
		}

		changes, err := currentTree.Diff(prevTree)
		if err != nil {
			return err
		}

		for _, c := range changes {
			...
		}

StevenACoffman avatar Nov 17 '19 22:11 StevenACoffman

@StevenACoffman, it's already about 1 year and I solved it different way. If you're interested I can check the code.

nerro avatar Nov 17 '19 23:11 nerro

Yeah, I would still really appreciate your code, if you can find it. Thanks very much!

StevenACoffman avatar Nov 18 '19 02:11 StevenACoffman

func main() {
	fmt.Println("git ")
	CheckArgs("<revision1>")

	var hash plumbing.Hash

	prevSha := os.Args[1] //prevSha

	dir, err := os.Getwd()
	CheckIfError(err)

	repo, err := git.PlainOpen(dir)
	CheckIfError(err)

	if len(os.Args) < 3 {
		headRef, err := repo.Head()
		CheckIfError(err)
		// ... retrieving the head commit object
		hash = headRef.Hash()
		CheckIfError(err)
	} else {
		arg2 := os.Args[2] //optional descendent sha
		hash = plumbing.NewHash(arg2)
	}

	prevHash := plumbing.NewHash(prevSha)

	prevCommit, err := repo.CommitObject(prevHash)
	CheckIfError(err)

	commit, err := repo.CommitObject(hash)
	CheckIfError(err)

	fmt.Println("Comparing from:"+ prevCommit.Hash.String() + " to:"+ commit.Hash.String())

	isAncestor, err := commit.IsAncestor(prevCommit)
	CheckIfError(err)

	fmt.Printf("Is the prevCommit an ancestor of commit? : %v %v\n",isAncestor)

	currentTree, err := commit.Tree()
	CheckIfError(err)

	prevTree, err := prevCommit.Tree()
	CheckIfError(err)

	patch, err := currentTree.Patch(prevTree)
	CheckIfError(err)
	fmt.Println("Got here"+ strconv.Itoa(len(patch.Stats())))

	var changedFiles []string
	for _, fileStat := range patch.Stats() {
		fmt.Println(fileStat.Name)
		changedFiles = append(changedFiles,fileStat.Name)
	}

	changes, err := currentTree.Diff(prevTree)
	CheckIfError(err)

	fmt.Println("Got here!")
	for _, change := range changes {
		// Ignore deleted files
		action, err := change.Action()
		CheckIfError(err)
		if action == merkletrie.Delete {
			//fmt.Println("Skipping delete")
			continue
		}

		// Get list of involved files
		name := getChangeName(change)
		fmt.Println(name)
	}
}

func getChangeName(change *object.Change) string {
		var empty = object.ChangeEntry{}
		if change.From != empty {
			return change.From.Name
		}

		return change.To.Name
}

StevenACoffman avatar Nov 18 '19 18:11 StevenACoffman

The change.Files() gives only the names of the files with to.Name, with no paths inside the repository, however the change.toString() gives the full path.

StevenACoffman avatar Nov 18 '19 18:11 StevenACoffman

@StevenACoffman this was an immensely helpful snippet of code to find. Do you think it could be moved into a test to ensure it doesn't bitrot?

achew22 avatar Mar 24 '20 06:03 achew22

Thanks! I have prototyped in bash and then usually convert to go, so I often search these issues for some equivalent. It would be nice to make a single page that is a translation between common git cheatsheet / cookbook and having go-git code snippets that did the same thing. Making them tests would keep ensure they are kept working though.

StevenACoffman avatar Mar 24 '20 13:03 StevenACoffman

godoc has a nifty thing called a "testable example" which you could use if you would like the test to run on a regular basis. I ended up reading a bunch of src-d/hercules code last night and they have an interesting thing where their test cases are based on the first 100 commits of the hercules repo. You could load the root of the git repo and use that as your testbed.

achew22 avatar Mar 24 '20 17:03 achew22

Go for it! I think you have a great idea, and I'd love to see it!

StevenACoffman avatar Mar 24 '20 22:03 StevenACoffman