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

Unshallow support

Open novas0x2a opened this issue 6 years ago • 4 comments

Currently, there's no way to directly unshallow a repo clone; the workaround is to fetch with Depth: <really-big-number>. It'd be nice if there were a way to do the equivalent of git fetch --unshallow

Thanks!

novas0x2a avatar May 19 '18 01:05 novas0x2a

@novas0x2a , did you test fetch with Depth: <really-big-number>? This doesn't seem to work for me. I am currently iterating over logs in a shallow clone which results in plumbing.ErrObjectNotFound (returned by the Next() func on iterators) if I hit the end of the shallow log. So then i attempt to fetch:

	if err == plumbing.ErrObjectNotFound {
		depth := 50
		err := repo.Fetch(&git.FetchOptions{Depth: depth})
		if err != nil {
			return "", fmt.Errorf("failed to fetch %d: %v", depth, err)
		}
	}

but this simply results in:

failed to fetch 50: already up-to-date

lucastheisen avatar Nov 29 '19 21:11 lucastheisen

50 isn't a particularly big number in this case. Did you try, like, 999999?

novas0x2a avatar Nov 29 '19 22:11 novas0x2a

Actually, I'm working on a very controlled test case. In the test, i commit 3 times, and do a git clone --depth 1 in order to mimic what our CI is doing. As such, the 50 should be plenty...

I walked through a lot of the fetch code and came across this code:

	req.Wants, err = getWants(r.s, refs)
	if len(req.Wants) > 0 {
		req.Haves, err = getHaves(localRefs, remoteRefs, r.s)
		if err != nil {
			return nil, err
		}

		if err = r.fetchPack(ctx, o, s, req); err != nil {
			return nil, err
		}
	}

It appears the r.fetchPack only occurs if getWants returns more than one. But getWants only seems to check the hash at the tip of each branch:

	wants := map[plumbing.Hash]bool{}
	for _, ref := range refs {
		hash := ref.Hash()
		exists, err := objectExists(localStorer, ref.Hash())
		if err != nil {
			return nil, err
		}

		if !exists {
			wants[hash] = true
		}
	}

So since its a shallow clone it has the same tip as the remote and no wants are detected.

I was able to work around this by creating a new remote... Its kinda crazy to have to do that though:

	if err == plumbing.ErrObjectNotFound {
		depth := 50
		remote, err := repo.Remote("origin")
		if err != nil {
			return "", fmt.Errorf("remote origin: %v", err)
		}
		if len(remote.Config().URLs) != 1 {
			return "", fmt.Errorf("origin urls %v", remote.Config())
		}
		r.SetRemote("foo", remote.Config().URLs[0])
		err = repo.Fetch(&git.FetchOptions{RemoteName: "foo", Depth: depth})
		if err != nil {
			return "", fmt.Errorf("failed to fetch %d, parent %v: %v", depth, parent, err)
		}
	} else if err != nil && err != versionChanged {
		return "", err
	}

UPDATE: Nevermind, this workaround doesn't help because it just compares the hash from the different remote with the local hashes... And its already there.

lucastheisen avatar Nov 29 '19 22:11 lucastheisen

Submitted #1250

lucastheisen avatar Nov 29 '19 23:11 lucastheisen