Git Data API
Github4s supports the Git Data API. As a result, with Github4s, you can:
- Get a Reference
- Create a Reference
- Update a Reference
- Get a Commit
- Create a Commit
- Get a Blob
- Create a Blob
- Get a Tree
- Create a Tree
- Create a Tag
For more information on the Git object database, please read the Git Internals chapter of the Pro Git book.
The following examples assume the following code:
import cats.effect.IO
import github4s.Github
import org.http4s.client.{Client, JavaNetClientBuilder}
val httpClient: Client[IO] = JavaNetClientBuilder[IO].create // You can use any http4s backend
val accessToken = sys.env.get("GITHUB_TOKEN")
val gh = Github[IO](httpClient, accessToken)
Git Data
References
Get a Reference
The ref must be formatted as heads/branch
, not just branch
.
For example, the call to get the data, the main
branch will be heads/main
.
If the ref
doesn’t exist in the repository, but existing refs
start with ref
they will be
returned as an array. For example, a call to get the data for a branch named feature
,
which doesn’t exist, would return head refs including featureA
and featureB
which do.
You can get a reference using getReference
, it takes as arguments:
- the repository coordinates (
owner
andname
of the repository). ref
: ref formatted asheads/branch
.pagination
: Limit and Offset for pagination, optional.
val getReference = gh.gitData.getReference("47degrees", "github4s", "heads/main")
getReference.flatMap(_.result match {
case Left(e) => IO.println(s"Something went wrong: ${e.getMessage}")
case Right(r) => IO.println(r)
})
The result
on the right is the corresponding List[Ref].
See the API doc for full reference.
Create a Reference
The ref must be formatted as heads/branch
, not just branch
.
For example, the call to get the data, the main
branch will be heads/main
.
You can create a reference using createReference
; it takes as arguments:
- the repository coordinates (
owner
andname
of the repository). ref
: The name of the fully qualified reference (e.g.:refs/heads/main
). If it doesn’t start with ‘refs’ and has at least two slashes, it will be rejected.sha
: the SHA1 value to set this reference.
val createReference = gh.gitData.createReference(
"47deg",
"github4s",
"refs/heads/main",
"d3b048c1f500ee5450e5d7b3d1921ed3e7645891")
createReference.flatMap(_.result match {
case Left(e) => IO.println(s"Something went wrong: ${e.getMessage}")
case Right(r) => IO.println(r)
})
The result
on the right is the created Ref.
See the API doc for full reference.
Update a Reference
You can update a reference using updateReference
; it takes as arguments:
- the repository coordinates (
owner
andname
of the repository). ref
: ref formatted as heads/branch.sha
: the SHA1 value to set this reference.force
: Indicates whether to force the update or to make sure the update is a fast-forward update. Setting it tofalse
will make sure you’re not overwriting work. Default:false
.
val updateReference = gh.gitData.updateReference(
"47deg",
"github4s",
"heads/main",
"d3b048c1f500ee5450e5d7b3d1921ed3e7645891",
false)
updateReference.flatMap(_.result match {
case Left(e) => IO.println(s"Something went wrong: ${e.getMessage}")
case Right(r) => IO.println(r)
})
The result
on the right is the updated Ref.
See the API doc for full reference.
Commits
Get a Commit
You can get a commit using getCommit
; it takes as arguments:
- the repository coordinates (
owner
andname
of the repository). sha
: the sha of the commit.
val getCommit = gh.gitData.getCommit("47degrees", "github4s", "d3b048c1f500ee5450e5d7b3d1921ed3e7645891")
getCommit.flatMap(_.result match {
case Left(e) => IO.println(s"Something went wrong: ${e.getMessage}")
case Right(r) => IO.println(r)
})
The result
on the right is the corresponding RefCommit.
See the API doc for full reference.
Create a Commit
You can create a commit using createCommit
; it takes as arguments:
- the repository coordinates (
owner
andname
of the repository). message
: the new commit’s message.tree
: the SHA of the tree object this commit points to.parents
: the SHAs of the commits that are the parents of this commit. If omitted or empty, the commit will be written as a root commit. For a single parent, an array of one SHA should be provided; for a merge commit, an array of more than one should be provided.author
: object containing information about the author.
val createCommit = gh.gitData.createCommit(
"47deg",
"github4s",
"New access token",
"827efc6d56897b048c772eb4087f854f46256132",
List("d3b048c1f500ee5450e5d7b3d1921ed3e7645891"),
None)
createCommit.flatMap(_.result match {
case Left(e) => IO.println(s"Something went wrong: ${e.getMessage}")
case Right(r) => IO.println(r)
})
The result
on the right is the created RefCommit.
See the API doc for full reference.
Blobs
Get a Blob
You can get a blob using getBlob
; it takes as arguments:
- the repository coordinates (
owner
andname
of the repository). sha
: the sha of the blob.
val getBlob = gh.gitData.getBlob("47degrees", "github4s", "d3b048c1f500ee5450e5d7b3d1921ed3e7645891")
getBlob.flatMap(_.result match {
case Left(e) => IO.println(s"Something went wrong: ${e.getMessage}")
case Right(r) => IO.println(r)
})
The result
on the right is the corresponding BlobContent.
See the API doc for full reference.
Create a Blob
You can create a blob using createBlob
; it takes as arguments:
- the repository coordinates (
owner
andname
of the repository). content
: the new blob’s content.encoding
: the encoding used for content. Currently, “utf-8” and “base64” are supported. Default: “utf-8”.
val createBlob = gh.gitData.createBlob("47degrees", "github4s", "New access token", Some("utf-8"))
createBlob.flatMap(_.result match {
case Left(e) => IO.println(s"Something went wrong: ${e.getMessage}")
case Right(r) => IO.println(r)
})
The result
on the right is the created RefObject.
See the API doc for full reference.
Trees
As you probably know, Git can be considered as a tree structure. Each commit creates a new node in that tree. We can even assume that all the Git commands or methods provided by the API are just tools to navigate this tree and to manipulate it.
In the following sections, we’ll see how Github4s provides methods to wrap the Git API.
Get a Tree
You can get a tree using getTree
; it takes as arguments:
- the repository coordinates (
owner
andname
of the repository). sha
: the sha of the commit.recursive
: flag whether to get the tree recursively.
val getTree = gh.gitData.getTree("47degrees", "github4s", "d3b048c1f500ee5450e5d7b3d1921ed3e7645891", true)
getTree.flatMap(_.result match {
case Left(e) => IO.println(s"Something went wrong: ${e.getMessage}")
case Right(r) => IO.println(r)
})
The result
on the right is the corresponding TreeResult.
See the API doc for full reference.
Create a Tree
The tree creation API will take nested entries as well. If both a tree and a nested entries modifying that tree are specified, it will overwrite the contents of that tree with the new path contents write out a new tree.
IMPORTANT: If you don’t set ´baseTree´, the commit will be created on top of everything; however, it will only contain your changes, the rest of your files will show up as deleted.
You can create a tree using createTree
; it takes as arguments:
- the repository coordinates (
owner
andname
of the repository). baseTree
: the SHA1 of the tree you want to update with new data.treeDataList
: list (of path, mode, type, and sha/blob) specifying a tree structure.path
: The file referenced in the tree.mode
: The file mode; one of 100644 for file (blob), 100755 for executable (blob), 040000 for subdirectory (tree), 160000 for submodule (commit), or 120000 for a blob that specifies the path of a symlink.type
: Either blob, tree, or commit.sha
: The SHA1 checksum ID of the object in the tree.content
: The content you want this file to have. GitHub will write this blob out and use that SHA for this entry. Use either this ortree.sha
.
import github4s.domain.TreeDataSha
val createTree = gh.gitData.createTree(
"47deg",
"github4s",
Some("827efc6d56897b048c772eb4087f854f46256132"),
List(TreeDataSha(
"project/plugins.sbt",
"100644",
"blob",
"827efc6d56897b048c772eb4087f854f46256132")))
createTree.flatMap(_.result match {
case Left(e) => IO.println(s"Something went wrong: ${e.getMessage}")
case Right(r) => IO.println(r)
})
The result
on the right is the created TreeResult.
See the API doc for full reference.
Tag
Create a Tag
You can create a tag using createTag
; it takes as arguments:
- the repository coordinates (
owner
andname
of the repository). tag
: the tag.message
: the new tag message.objectSha
: the SHA of the git object this is tagging.objectType
: the type of the object we’re tagging. Normally this is acommit
, but it can also be atree
or ablob
.tagger
: Optional object containing information about the individual creating the tag.
import github4s.domain.RefAuthor
val createTag = gh.gitData.createTag(
"47deg",
"github4s",
"v0.1.1",
"New access token",
"d3b048c1f500ee5450e5d7b3d1921ed3e7645891",
"commit",
Some(RefAuthor("2014-11-07T22:01:45Z", "rafaparadela", "developer@47deg.com")))
createTag.flatMap(_.result match {
case Left(e) => IO.println(s"Something went wrong: ${e.getMessage}")
case Right(r) => IO.println(r)
})
The result
on the right is the created Tag.
See the API doc for full reference.
As you can see, a few features of the git data endpoint are missing.
As a result, if you’d like to see a feature supported, feel free to create an issue and/or a pull request!