Giter Club home page Giter Club logo

go-git's Introduction

go-git logo GoDoc Build Status Go Report Card

go-git is a highly extensible git implementation library written in pure Go.

It can be used to manipulate git repositories at low level (plumbing) or high level (porcelain), through an idiomatic Go API. It also supports several types of storage, such as in-memory filesystems, or custom implementations, thanks to the Storer interface.

It's being actively developed since 2015 and is being used extensively by Keybase, Gitea or Pulumi, and by many other libraries and tools.

Project Status

After the legal issues with the src-d organization, the lack of update for four months and the requirement to make a hard fork, the project is now back to normality.

The project is currently actively maintained by individual contributors, including several of the original authors, but also backed by a new company, gitsight, where go-git is a critical component used at scale.

Comparison with git

go-git aims to be fully compatible with git, all the porcelain operations are implemented to work exactly as git does.

git is a humongous project with years of development by thousands of contributors, making it challenging for go-git to implement all the features. You can find a comparison of go-git vs git in the compatibility documentation.

Installation

The recommended way to install go-git is:

import "github.com/go-git/go-git/v5" // with go modules enabled (GO111MODULE=on or outside GOPATH)
import "github.com/go-git/go-git" // with go modules disabled

Examples

Please note that the CheckIfError and Info functions used in the examples are from the examples package just to be used in the examples.

Basic example

A basic example that mimics the standard git clone command

// Clone the given repository to the given directory
Info("git clone https://github.com/go-git/go-git")

_, err := git.PlainClone("/tmp/foo", false, &git.CloneOptions{
    URL:      "https://github.com/go-git/go-git",
    Progress: os.Stdout,
})

CheckIfError(err)

Outputs:

Counting objects: 4924, done.
Compressing objects: 100% (1333/1333), done.
Total 4924 (delta 530), reused 6 (delta 6), pack-reused 3533

In-memory example

Cloning a repository into memory and printing the history of HEAD, just like git log does

// Clones the given repository in memory, creating the remote, the local
// branches and fetching the objects, exactly as:
Info("git clone https://github.com/go-git/go-billy")

r, err := git.Clone(memory.NewStorage(), nil, &git.CloneOptions{
    URL: "https://github.com/go-git/go-billy",
})

CheckIfError(err)

// Gets the HEAD history from HEAD, just like this command:
Info("git log")

// ... retrieves the branch pointed by HEAD
ref, err := r.Head()
CheckIfError(err)


// ... retrieves the commit history
cIter, err := r.Log(&git.LogOptions{From: ref.Hash()})
CheckIfError(err)

// ... just iterates over the commits, printing it
err = cIter.ForEach(func(c *object.Commit) error {
	fmt.Println(c)
	return nil
})
CheckIfError(err)

Outputs:

commit ded8054fd0c3994453e9c8aacaf48d118d42991e
Author: Santiago M. Mola <[email protected]>
Date:   Sat Nov 12 21:18:41 2016 +0100

    index: ReadFrom/WriteTo returns IndexReadError/IndexWriteError. (#9)

commit df707095626f384ce2dc1a83b30f9a21d69b9dfc
Author: Santiago M. Mola <[email protected]>
Date:   Fri Nov 11 13:23:22 2016 +0100

    readwriter: fix bug when writing index. (#10)

    When using ReadWriter on an existing siva file, absolute offset for
    index entries was not being calculated correctly.
...

You can find this example and many others in the examples folder.

Contribute

Contributions are more than welcome, if you are interested please take a look to our Contributing Guidelines.

License

Apache License Version 2.0, see LICENSE

go-git's People

Contributors

abhinav avatar ajnavarro avatar alcortesm avatar ariehschneier avatar aymanbagabas avatar cristaloleg avatar darkowlzz avatar dependabot[bot] avatar erizocosmico avatar ferhatelmas avatar filipnavara avatar ga-paul-t avatar ilius avatar jfontan avatar joshuasjoding avatar mcuadros avatar muesli avatar mvdan avatar orirawlings avatar pjbgf avatar saracen avatar serabe avatar smola avatar strib avatar taruti avatar toqueteos avatar twpayne avatar vancluever avatar vmarkovtsev avatar zeripath avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

go-git's Issues

Commit.Patch() has dramatically different performance on two nearly-identical repos

Describe the bug

I have two repos called ABC and ABC-clone and they are ostensibly equivalent (please see below for a detailed comparison). When I invoke Commit.Patch() (https://github.com/go-git/go-git/blob/master/plumbing/object/commit.go#L101-L107) on both repos in a server-side pre-receive hook on GitHub Enterprise using the same arguments, the performance is dramatically slower for ABC compared to ABC-clone. During my most recent measurement, the execution of Commit.Patch() for ABC took 4 seconds 228 milliseconds 541 microseconds, whereas the execution for ABC-clone took only 59 milliseconds 463 microseconds.

Does anyone know why go-git might have different performance on my two ostensibly equivalent repos? This slow performance will unfortunately prohibit me from using go-git for my specific use case.

Expected behavior

I'd expect go-fit to have similar fast performance for both repos (< 1 second for my particular commit). I would not expect Gitleaks to take > 3 seconds for the ABC repo.

Basic info

  • Env: Pre-receive hook FireJail/chroot sandboxgith for GitHub Enterprise server hosted on Google Cloud
  • Host kernel: Linux 4.9.0-12-amd64
  • Host architecture: x86-64
  • Host OS: Debian GNU/Linux 9.12 (stretch)
  • Go-git Version: v5.0.0

Additional context

Are the repos actually the same?

  • Similarity: when I visit the repos in the browser, they appear to be the same, because they both have 3,163 commits and 330 branches. The repos have the same content (ie. git show COMMIT_ID will return the same output for both repos).
  • Difference: when I investigate the server-side instances of the repos and I execute echo -e $(du -sh *) in the .git directory, the repos appear quite different. The ABC repo (slow) has a server-side .git/objects directory of 1.0 MB whereas the ABC-clone repo (fast) has a server-side .git/objects size of 11.5 MB.....so I'm very confused. I would have expected their .git/objects directories to be the same size. See my du output below. My hypothesis is that, due to the gradual organic way in which the ABC repo was created over the course of a few years, compared to the rapid way in which I cloned it in a few minutes (using a combination of git remote update, git fetch --all, git pull --all, git branch --track, git push --all, etc.), the ABC repo more efficiently re-uses the same objects, whereas the ABC-clone repo probably has some duplicate objects that could be garbage collected with something like git gc. I'm still digging into this a bit.
  • Difference: the ABC repo (slow) has a few different settings in GitHub Enterprise than the ABC-clone repo. Specifically, the ABC repo has a few webhooks installed and a few install GitHub Apps, whereas the ABC-clone repo has no webhooks and no apps installed. I'm investigating the behavior of the apps currently.

Are the runtime environments the same, when invoking the Commit.Patch() for both repos? Do both invocations have similar CPU, RAM, disk, etc. resources?

Yes. I've done a modest amount of benchmarking, and all my tests indicate that both invocations have similar resources.

  • IMPORTANT: notably git show COMMIT_ID runs just as fast server-side in both repos when I timed it for the same commit id!!!
  • I used another test to very very roughly benchmark multiplication and division operations to ensure that CPU/ALU resource availability was approximately equivalent

Output of du -sh .git/*

ABC (slow)                     ABC-clone (fast)
=================================================
4.0K FETCH_HEAD                -
4.0K HEAD                      4.0K HEAD
6.8M audit_log                 400.0K audit_log
4.0K config                    4.0K config
4.0K description               4.0K description
4.0K dgit-state                4.0K dgit-state
0 dgit-state.flock             0 dgit-state.flock
0 hooks                        0 hooks
20.0K info                     12.0K info
4.0K language-stats.cache.     4.0K language-stats.cache
2.8M logs                      1.3M logs
0 nw-sync.lock                 -
1.0M objects                   11.5M objects
88.0K packed-refs              4.0K packed-refs
4.0K refs                      1.3M refs
-                              0 nw-repack.flock

What commit am I testing with?

The commit that I'm testing with is very small. For privacy, I've obfuscated it heavily, but the key aspects (size of changes, number of files modified, type of files, etc.) are the same.

commit c372960a4124f724e5b1c967c2913829f8e1eea1 (origin/some-branch-3)
Author: OMITTED <OMITTED>
Date:   OMITTED

    normal commit message blah blah blah

diff --git a/foo/bar/baz/qux-quux/corge.yaml b/foo/bar/baz/qux-quux/corge.yaml
index cc3fd755..4af4d258 100644
--- a/foo/bar/baz/qux-quux/corge.yaml
+++ b/foo/bar/baz/qux-quux/corge.yaml
@@ -30,7 +30, @@ egress:
      ijk.xyz.foobar.io/prox-body-key: "10"
      ijk.xyz.foobar.io/prox-read-limit: "900"
      ijk.xyz.foobar.io/prox-send-limit: "900"
-     ijk.xyz.foobar.io/prox-src-max-age: "100"
+     ijk.xyz.foobar.io/prox-src-max-age: "90"
   hosts:
     - waldo.fred.com
   tls:
@@ -53,7 +53,7 @@ abc:
   xyz:
     name: abc-xyz
     resources:
-      requests: 40G
+      requests: 200G

 variables: {}

Contribute end-to-end example for branch/commit/push

The examples here while useful, spread out the set of steps required to accomplish the following workflow:

  • Clone project
  • Checkout a branch
  • {Modify files}
  • Commit changes
  • Push to remote branch

I think having such an end-to-end example would help new users get started quickly if they're trying to automate a workflow they have using this library. I'm happy to contribute such an example but unsure of where to put it. Is showcase the best place, or should I create a new directory to outline this use case?

Correctly add submodules

I've noticed that after updating a submodule, for instance getting the repository and doing a pull, adding it to the worktree instead adds all of the descendant files. This doesn't seem correct as a git status still shows it as not added to the index.

Support `git rev-parse --show-toplevel`

If I have a repository at ~/foo, I can run git rev-parse --show-toplevel in ~/foo/bar without a problem to find the root of the current repository (~/foo).

This was mentioned way back in src-d/go-git#765

However, the fine folks at Google worked around this issue by the code here:
GoogleContainerTools/skaffold#275

And I think it would be handy to include this commonly needed functionality in go-git itself so people didn't need to rely on go-findroot and the presence of the git binary to achieve this.

Repository.CreateBranch fails if a remote branch exists with the same name

I tried to create a local branch based on a remote branch by doing this:

err = repo.CreateBranch(&gitconfig.Branch{Name: b, Remote: "origin", Merge: plumbing.NewBranchReferenceName(b)})

However it returns ErrBranchExists even though the local branch doesn't exist. It looks like it is comparing based on name alone, not whether it is local or remote.

I instead got it to work following _example/branch:

targetRef, err := repo.Reference(plumbing.NewRemoteReferenceName("origin", b), true)
if err != nil {
	t.Fatalf("Couldn't get reference for remote branch: %v", err)
}
ref := plumbing.NewHashReference(plumbing.ReferenceName("refs/heads/"+b), targetRef.Hash())
err = repo.Storer.SetReference(ref)

Have I misunderstood something, or is it a bug that CreateBranch doesn't differentiate between local and remote branches?

Submodules Updates do not pull tags

Hello,

I'am trying to use the Submodules feature.
I have one repository with one sub-module. In this sub-module I have one tag.

First I clone the repository:

git_repo, err := git.PlainClone(localFolder, false, &git.CloneOptions{
    URL: "https://MY-REPO.git",
    Auth: &http.BasicAuth{
        Username: MY_USERMANE,
        Password: MY_PASSWORD,
    },
    ReferenceName: plumbing.ReferenceName(fmt.Sprintf("refs/heads/%s", "release")),
})

This works great, I have the repository on the right branch with the right .gitmodules

I update the sub-module:

subs, err := tri.Submodules()
HandleError(err)
subs.Update(&git.SubmoduleUpdateOptions{
    Init:              true,
    RecurseSubmodules: 0,
    Auth: &http.BasicAuth{
        Username: MY_USERNAME,
        Password: MY_PASSWORD,
    },
    NoFetch: false,
})

The sub-module has been pulled from the remote server.
But when I go in the sub-module I have no tags:

$ git tag

$ git branch
* (no branch)

If I update the sub-modules from bash, here is the result:

$ git submodule update --init
$ git tag
release
$ git branch
* (detached from 849cc28)
  master

Looks like the update is not pulling all the information as the bash command.

I used those function by reading the source code so I don't know I missed something.
How could I retrieve the git tags after updated my sub-modules ?

Thanks

[Example] How to cat-file from remote repository?

Hey,
According to the compatibility list, it is possible to make a cat-file on a remote repository, but I have looked at the documentation and partly the source code and I do not see such a possibility. Would it be possible to add an example of how to do it?

In one of the projects I have to search a large repository and fetch each branch to check the content of one file is very slow, and sometimes we get strange errors (e.g. local branches become damaged), so the best method seems to be cat-file but I would like to done from within code instead of calling git externally.

P.S. It's great that you continue the project!

How to find the commit referenced by a tag?

A tag is a *plumbing.Reference, the hash points to the tag itself, not the commit, how do I find the commit referenced by the tag?

Also, using git I can find the commit hash by git rev-list -n 1 TAG, but go-git doesn't seem have corresponding API

Confusion over RefSpec

RefSpec is a mapping from local branches to remote references. The format of the refspec is an optional +, followed by :, where is the pattern for references on the remote side and is where those references will be written locally. The + tells Git to update the reference even if it isn’t a fast-forward. eg.: "+refs/heads/:refs/remotes/origin/"

Shouldn't src be on the local side not remote? And shouldn't dst be where the remote is written? The example also hints at that. This in context to git.PushOptions.

PlainClone: reference not found

I'm getting reference not found when I try to clone an empty repository.

...
log.Println("Parsing Private Key...")
signer, err := ssh.ParsePrivateKey([]byte(GOOGLE_ID_RSA))
if err != nil {
	log.Printf("Failed to parse key: %s",
		err.Error())
	return
}
log.Println("Done")

auth := &git_ssh.PublicKeys {
	User: "git",
	Signer: signer,
}
auth.HostKeyCallback = ssh.InsecureIgnoreHostKey()

clone_path := "/tmp/<reponame>"
clone_url := "ssh://<email>@source.developers.google.com:2022/p/<project>/r/<reponame>"

log.Printf("Cloning repo '%s' to '%s'...", clone_url, clone_path)
repo, err := git.PlainClone(clone_path, false, &git.CloneOptions{
	URL: clone_url,
	Progress: os.Stdout,
	Auth: auth,
})
if err != nil {
	log.Printf("Failed to clone repo: %s",
		err.Error())
	return
}
log.Println("Done")

log.Println("Getting working tree...")
work_tree, err := repo.Worktree()
...

Output:

2020/04/06 17:48:05 Decoding JSON...
2020/04/06 17:48:05 Done
2020/04/06 17:48:05 Parsing Private Key...
2020/04/06 17:48:05 Done
2020/04/06 17:48:05 Cloning repo 'ssh://<email>@source.developers.google.com:2022/p/<project>/r/<reponame>' to '/tmp/<reponame>'...
2020/04/06 17:48:06 Failed to clone repo: reference not found

I tried it on a different repo with an initial commit committed and it worked fine.

This is what I have imported:

	"github.com/go-git/go-git/v5"
	"github.com/go-git/go-git/v5/plumbing"
	git_ssh "github.com/go-git/go-git/v5/plumbing/transport/ssh"
	git_object "github.com/go-git/go-git/v5/plumbing/object"

Does go-git supports Credential-Helper scripts?

Does go-git supports authentication other than basicAuth? I am looking to connect to a private repository using git credentials helper script so that I dont need to pass username and password.

Is it feasible with go-git library?

Rename (object path): Access is denied when trying to stage files

This one was unexpected as I just set up a completely blank repo with one commit using Git version 2.17.1.windows.2:

~/Desktop
 mkdir tmpo

    Directory: C:\Users\Southclaws\Desktop

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----

~/Desktop
 cd .\tmpo\

~/Desktop/tmpo
 git init
Initialized empty Git repository in C:/Users/Southclaws/Desktop/tmpo/.git/

tmpo
 git remote add origin https://a-git-repo-i-have-access-to

tmpo
 New-Item .gitkeep

    Directory: C:\Users\Southclaws\Desktop\tmpo

-a----        05/05/2020     16:41              0 .gitkeep

tmpo
 git commit
[master (root-commit) 2651a57] one
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 .gitkeep

Permissions seem fine, I have full access since I created the repo from the same account I am trying to run my own go-git binary from:

tmpo on  master
 (get-acl .).access | ft IdentityReference,FileSystemRights,AccessControlType,IsInherited,InheritanceFlags -auto

IdentityReference         FileSystemRights AccessControlType IsInherited                InheritanceFlags
-----------------         ---------------- ----------------- -----------                ----------------
BUILTIN\Users          Modify, Synchronize             Allow        True ContainerInherit, ObjectInherit
NT AUTHORITY\SYSTEM            FullControl             Allow        True ContainerInherit, ObjectInherit
BUILTIN\Administrators         FullControl             Allow        True ContainerInherit, ObjectInherit
CAURINUS\Southclaws            FullControl             Allow        True ContainerInherit, ObjectInherit

And here's the code I quickly wrote for a small proof-of-concept project:

package main

import (
	"os"

	"github.com/go-git/go-git/v5"
	"github.com/go-git/go-git/v5/plumbing/transport/http"
)

func main() {
	if err := run(); err != nil {
		panic(err)
	}
}

func run() error {
	wd, err := os.Getwd()
	if err != nil {
		panic(err)
	}
	repo, err := git.PlainOpen(wd)
	if err != nil {
		panic(err)
	}
	wt, err := repo.Worktree()
	if err != nil {
		panic(err)
	}
	if err := wt.AddGlob("*"); err != nil {
		panic(err)
	}
	if _, err := wt.Commit("test", nil); err != nil {
		panic(err)
	}
	if err := repo.Push(&git.PushOptions{
		RemoteName: "origin",
		Auth: &http.BasicAuth{
			Username: "Southclaws",
			Password: "a password good enough to post on a github issue",
		},
	}); err != nil {
		panic(err)
	}
	return nil
}

And here's the error that was thrown by the wt.AddGlob call:

panic: rename C:\Users\Southclaws\Desktop\tmpo\.git\objects\pack\tmp_obj_261084259 C:\Users\Southclaws\Desktop\tmpo\.git\objects\e6\9de29bb2d1d6434b8b29ae775ad8c2e48c5391: Access is denied.

goroutine 1 [running]:
main.run(0x7ce040, 0xc000020118)
        E:/Work/gogitpoc/main.go:30 +0x31e
main.main()
        E:/Work/gogitpoc/main.go:11 +0x29

plumbing/object: add a chronological commit iterator

I upstreamed a "post order" iterator a while ago, under the assumption that it was what git log does by default. That's wrong :) In particular, I mistakenly assumed that post-order would equal chronological order, but that's not the case.

From man git-log:

By default, the commits are shown in reverse chronological order.

That is, if a merge commit is merging a very old commit onto a recent base, git log will show:

  • merge commit
  • recent base
  • (some less recent commits here)
  • very old commit

Our post-order iterator would do what it's designed to do, which is not chronological:

  • merge commit
  • very old commit
  • recent base
  • (some less recent commits here)

We should add an iterator that does what git log does. I have an implementation that I'm happy to upstream, if that sounds like a good idea.

My implementation only does chronological sorting of the commits in the "stack" while walking, it doesn't sort the entire commit history at once. It's very likely that git log does the same, as otherwise it would have to load the entire commit history into memory.

I'm not sure if the post-order iterator is really useful for anything, to be honest, but I guess we can leave it there.

Worktree.Commit() read git config user data

Hi,

i want to do a Git commit from my code programmatically, without having the user to provide any information, like CommitOptions. As with standard git command, i would expect git to fall back to globally configured user.name and user.email. Can this be done also with go-git?

P.S.: there is a segfault in Worktree.Commit() if nil passed for &CommitOptions.

Git Log Revision Range

Found this old Issue on the old repo: src-d/go-git#1166.
It would be super useful for something like git log HEAD..origin/master to be added to git.LogOptions as this would make working with Git Logs much easier.

[Q] Filter commits by author.

Hi,
How can I filter commits by the author?

I looked into Log, but LogOptions doesn't contain filed author.
I would like to archive similar functionality to git log --author='<author>'

Thanks in advance.

how to git log between two branches

Is there a way to do the equivalent of git log master...my/branch?
Very new to this package, but really only seeing timestamps as a way to limit log results.

My goal is to iterate through all the commit messages between my current branch and master

CI tests look flaky

go-git's CI often fails with errors like:

----------------------------------------------------------------------
##[error]FAIL: <autogenerated>:1: ReceivePackSuite.TestAdvertisedReferencesEmpty

/Users/runner/runners/2.169.1/work/go-git/go-git/plumbing/transport/test/receive_pack.go:35:
    c.Assert(err, IsNil)
... value *net.OpError = &net.OpError{Op:"dial", Net:"tcp", Source:net.Addr(nil), Addr:(*net.TCPAddr)(0xc0000f6c90), Err:(*os.SyscallError)(0xc00015a000)} ("dial tcp [::1]:49628: connect: connection refused")

OOPS: 28 passed, 1 FAILED
--- FAIL: Test (9.96s)
FAIL
coverage: 10.6% of statements in ./...
FAIL	github.com/go-git/go-git/v5/plumbing/transport/git	11.449s

Example: https://github.com/go-git/go-git/pull/47/checks?check_run_id=634865225

This looks like a race between a test server starting up and a client trying to connect to it.

Presumably this is already known (although I could not find any existing open issue for it either here or at https://github.com/src-d/go-git).

Questions:

  • Is there any existing effort to fix these tests or any background information about them?
  • Would you appreciate help fixing them? I can't promise any solutions, but might be able to have a poke around.

Getting line number in commit

Is it possible to get line numbers of commits into go-git?
If not, is it something that is planned to be added in the future?

How to check if branch is tracking an upstream?

I'd like to check if a branch is tracking an upstream branch, and if it is, what's the name of the upstream.

Something similar to calling:

git rev-parse --abbrev-ref  master@{upstream}
  origin/master

git rev-parse --abbrev-ref  foo@{upstream}
  fatal: no upstream configured for branch 'foo'

or to the info in the square brackets in the output of:

git branch -vv
  development a0f09ef [origin/development] commit message
* master      a530b15 [origin/master: ahead 4] commit message

worktree.Status not honouring .gitignore

I have a function that checks the working tree's current status:

status, err := worktree.Status()
// more stuff

in a particular git repo, I have a node_modules directory in a sub directory:

sdk/nodejs/node_modules

I added the following to my .gitignore

**/node_modules

Then I ran status.IsClean()

However, for whatever reason, the `.gitignore is not being honoured for 2 files:

?? sdk/nodejs/node_modules/yn/index.js
?? sdk/nodejs/node_modules/yn/package.json

No matter what I do (including explicitly adding sdk/nodejs/node_modules/yn/index.js to the .gitignore` the working tree comes back dirty. Any thoughts?

Checkout multiple branches

With plain git, I can checkout a branch to another folder, outside of the repository:

git worktree add /path/to/somewhere branch

This gives me branch checked out to /path/to/somewhere. I would like to do the same thing with go-git. Correct me if I'm wrong, but I don't think it's possible yet?

Checkout to tag hash is not working

Hello,

Checking out from a tag hash is not working.

Example:

gitTag, err := repo.Tag(ref)
if err != nil {
	return nil, err
}

workTree, err := repo.Worktree()
if err != nil {
	return nil, err
}


err = workTree.Checkout(&git.CheckoutOptions{
		Hash:   gitTag.Hash(),
})

An Error will occur because the Tag Hash is not in the working tree.
I don't know why but the hash returned by gitTag.Hash() is not the commit Hash.

Tag Hash: d98ac2f9fa70e6bba8bd17f23be96b52608792f2
Commit: ebee1b73ea6315e0c7b27f6bb3a88df27d6fec62

So an error "Object not found" occurs.

But taking this Hash, and doing a checkout with git CLI is working:

$ git checkout d98ac2f9fa70e6bba8bd17f23be96b52608792f2
HEAD is now at ebee1b7... release
$ git log
commit ebee1b73ea6315e0c7b27f6bb3a88df27d6fec62

Regards,

Log in reverse order

I'm looking for the equivalent of git log --reverse, or another way to get all commits starting with the first. LogOptions doesn't seem to have anything like this.

Pull-FF bug in merge-reset functionality

When you try to do git pull fast forward and you have modifies unstaged file, which is not affected by new commits which comes from remote, fast forward pull operation should be successful. At least native git handles it in this way.
The line

unstaged, err := w.containsUnstagedChanges()
makes a very nasty behavior in this case. Worktree of the git repo remains untouched, HEAD points to the latest commit, which came from the remote, and files which are modifies by pulled commits, are staged without changes. So if user now commit the state of worktree, all pulled changes will be reverted.

Instead of using w.containsUnstagedChanges() there should be a check for a files with FileStatus Unmodified for staging and Modified for Worktree. And only in this case to stop the reset. Because case when FileStatus is Modified in staging and unmodified in worktree is actually okay for a merge reset.

worktree.Add() is very slow

I have a commit that has about 2 hundred files in it. Adding files is really slow -- each call to add takes a substantial portion of a second (typically 200-500 msec) -- my repo is large.

There was a similar issue filed against the src-d repo -- claiming that Add() calls Status() each time, which makes this an O(N^2) algorithm.

Pull removes untracked files locally

Hi,

Thank you for the great work you've been doing with go-git. I've used it to start a new project and it works really well.

As a new user I guess I might be doing something wrong, or miss a concept, but here I have a very basic Pull operation on a repository which looks like this:

if err := w.Pull(&gogit.PullOptions{}); err != nil {
	if err == gogit.NoErrAlreadyUpToDate {
		return nil
	}
	return err
}

The folder into which the repository lives also contains untracked files. If I run this pull operation with go-git, all the untracked files are being removed from the directory. On the other hand if run a manual git pull in my terminal from the same folder, untracked files get preserved as expected.

Could you please help me ?

Thanks in advance

Fetching commits directly via "git fetch ... SHA:..."

See git/git@f8edeaa for an example of the functionality I'm talking about -- a prominent example of usage is in https://github.com/actions/checkout (v2) for being able to fetch the relevant commit for a workflow even if the branch has been deleted (GitHub didn't support doing this via git fetch previously, but I think that GitHub Actions has prompted a change, especially since "dangling" commits have always been accessible from the web UI if you had or could craft an appropriate URL to them).

I've tried a couple permutations of RefSpec syntax to see if it was already supported (and read through docs), but couldn't get anything working so I think it's not supported? (but happy to be proved wrong! 😅 ❤️)

For now I've gone with the workaround of shelling out to git fetch for the place that I need this. 👍

Thanks for your work on this awesome Git implementation! ❤️ 👍

Move over issues with GitHub API

Thanks for continuing to maintain this, it's appreciated.

Are there any plans to (at least selectively) move issues over from src-d/go-git using the GitHub API, and potentially notify PR authors to move their PRs to here? For example src-d/go-git#1273 (a PR, not an issue, but related) is something I put up a while ago.

All files in git should be with LF newlines

Currently some files are with CRLF newlines.

From gitea ci build:

warning: CRLF will be replaced by LF in vendor/github.com/go-git/go-git/v5/plumbing/format/commitgraph/commitgraph.go.
warning: CRLF will be replaced by LF in vendor/github.com/go-git/go-git/v5/plumbing/format/commitgraph/encoder.go.
warning: CRLF will be replaced by LF in vendor/github.com/go-git/go-git/v5/plumbing/format/commitgraph/file.go.
warning: CRLF will be replaced by LF in vendor/github.com/go-git/go-git/v5/plumbing/format/commitgraph/memory.go.
warning: CRLF will be replaced by LF in vendor/github.com/go-git/go-git/v5/plumbing/object/commitgraph/commitnode.go.
warning: CRLF will be replaced by LF in vendor/github.com/go-git/go-git/v5/plumbing/object/commitgraph/commitnode_graph.go.
warning: CRLF will be replaced by LF in vendor/github.com/go-git/go-git/v5/plumbing/object/commitgraph/commitnode_object.go.
warning: CRLF will be replaced by LF in vendor/github.com/go-git/go-git/v5/plumbing/object/commitgraph/commitnode_walker_ctime.go.
warning: CRLF will be replaced by LF in vendor/github.com/toqueteos/trie/LICENSE.txt.

More info: https://drone.gitea.io/go-gitea/gitea/23101/1/4

How to avoid file mode changes in go-git?

👍 Awesome project! it helped me a lot.
Currently, I'm facing the file mode changing issues in my project.

old mode 100644
new mode 100755

Per the "How do I make Git ignore file mode (chmod) changes?" q&a in StackOverflow, I tried to set the filemode setting to false into the git config file under the cloned repo. But it doesn't work.

$ cat .git/config
[core]
        filemode = false
        bare = false
[remote "origin"]
        url = https://xxxx.git
        fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
        remote = origin
        merge = refs/heads/master

I also found a similar issue in the old repo: Changing file mode on NTFS #866
Did anyone know how to avoid this kind of issue in go-git? Thank you!

Blame is very slow

I tested a Blame and as compared to CLI it is very slow. CLI takes around < 1 and go-git takes 35 second.

Worktree Checkout behavior is different from git

From what I've noticed, the Checkout method on Worktree behaves differently than standard git.

For example, given an untracked file in the working directory of some branch tree, calling Checkout with the default options, to another branch, would remove that file, which would not happen with the standard git checkout other-branch.

Using the Keep option wouldn't help much, as files that were added and committed would remain in the working directory after a checkout.

I suppose this has to do with the current implementation where the Checkout acts somehow like git reset.

Is this expected or is it something to look into?
Thanks!

Commit --amend and --no-edit

Hi. I'm trying to implement a simple git commit --amend --no-edit using go-git. I noticed the compatibility table states commit is supported, without any notes on specific flags. Are the flags --amend and --no-edit implemented and I'm missing something? If this is not currently possible with this module, maybe the documentation could state it upfront.

Implement smudge and clean filters

I'd like to get LFS working with go-git and the first step to that is smudge and clean filters.

I'm not that familiar with the codebase or how Git invokes clean/smudge filters but with some direction I'd be up for implementing it.

Random EOF errors when performing clone

I am performing in memory git clones as follows

	// creating the in mem fs to hold the checkouts
	fs := memfs.New()

	_, err = git.Clone(memory.NewStorage(), fs, &git.CloneOptions{
		URL:           repo,
		ReferenceName: plumbing.ReferenceName(branch),
		Depth:         1,
		Auth:          auth,
	})

	if err != nil {
		log.Fatalf("ERROR Cloning %s and branch %s: %s\n", repo, branch, err.Error())
	}

The problem is that sometimes I get the following error:

ERROR Cloning [email protected]:my-username/my-repo.git and
branch refs/heads/workablestg1: unexpected EOF

is there an easy way to do git checkout?

In go-git, there are quite different ways to checkout:

  1. rev with SHA
  2. tag name
  3. branch name

And with my test, there are bugs in the examples to deal with above checkout.

What I would like to achieve is to checkout a generic name, this name could be one of above, in essence, it is exactly what: git checkout does

Just wondering if this could be wrapped up with one function instead of dealing with different func calls like above.

Thanks

Push results to EOF on large repositories

Hi!

I'm currently working on a small and simple tool for syncing my forks utilizing this library which can be found here: https://github.com/topikettunen/gitsyncr. The tool simply just pulls the latest changes from upstream and then pushes them to my fork.

I stumbled upon this problem when I was trying to push the latest changes from the Linux kernel's upstream to my fork using my tool which then resulted in the following output: https://github.com/topikettunen/gitsyncr/issues/1.

Code in question is:

func pushChanges(url, path string, user user, publicKey *ssh.PublicKeys) {
	r, err := git.PlainOpen(path)
	if err != nil {
		log.Fatal(err)
	}
	pushOpts := pushOpts(url, user, publicKey)
	err = r.Push(&pushOpts)
	if err == git.NoErrAlreadyUpToDate {
		log.Printf("%s already up to date...\n", url)
	} else if err != nil {
		log.Fatal(err)
	}
}

PushOptions used:

if strings.Contains(url, "git://") {
	pushOpts = git.PushOptions{
		RemoteName: "fork",
		Progress: os.Stdout,
	}
} else {
	pushOpts = git.PushOptions{
		RemoteName: "fork",
		Auth: publicKey,
		Progress: os.Stdout,
	}
}

The function didn't seem to have any problems when pushing changes for other my forks, e.g. Kubernetes which is also a quite large repository. But this error arises only with this Linux kernel fork. I was thinking could it be something related to different protocols used, since I used Git protocol for the kernel's upstream and SSH to push to the fork, but after testing with the Linux's GitHub mirror which uses SSH, it resulted in the same error. Although cloning and pulling from the upstream worked for the Linux kernel, only the push failed. This then led me to believe this is something related to large repositories.

Am I doing something wrong or do you know the answer to this?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.