forked from Gitlink/gitea-1156
update code.gitea.io/git (#450)
This commit is contained in:
parent
0c5c34d7dd
commit
47a7529d96
|
@ -121,7 +121,7 @@ func handleUpdateTask(uuid string, user, repoUser *models.User, reponame string,
|
||||||
|
|
||||||
// Ask for running deliver hook and test pull request tasks.
|
// Ask for running deliver hook and test pull request tasks.
|
||||||
reqURL := setting.LocalURL + repoUser.Name + "/" + reponame + "/tasks/trigger?branch=" +
|
reqURL := setting.LocalURL + repoUser.Name + "/" + reponame + "/tasks/trigger?branch=" +
|
||||||
strings.TrimPrefix(task.RefName, git.BRANCH_PREFIX) + "&secret=" + base.EncodeMD5(repoUser.Salt) + "&pusher=" + com.ToStr(user.ID)
|
strings.TrimPrefix(task.RefName, git.BranchPrefix) + "&secret=" + base.EncodeMD5(repoUser.Salt) + "&pusher=" + com.ToStr(user.ID)
|
||||||
log.GitLogger.Trace("Trigger task: %s", reqURL)
|
log.GitLogger.Trace("Trigger task: %s", reqURL)
|
||||||
|
|
||||||
resp, err := httplib.Head(reqURL).SetTLSClientConfig(&tls.Config{
|
resp, err := httplib.Head(reqURL).SetTLSClientConfig(&tls.Config{
|
||||||
|
|
|
@ -494,12 +494,12 @@ func CommitRepoAction(opts CommitRepoActionOptions) error {
|
||||||
isNewBranch := false
|
isNewBranch := false
|
||||||
opType := ActionCommitRepo
|
opType := ActionCommitRepo
|
||||||
// Check it's tag push or branch.
|
// Check it's tag push or branch.
|
||||||
if strings.HasPrefix(opts.RefFullName, git.TAG_PREFIX) {
|
if strings.HasPrefix(opts.RefFullName, git.TagPrefix) {
|
||||||
opType = ActionPushTag
|
opType = ActionPushTag
|
||||||
opts.Commits = &PushCommits{}
|
opts.Commits = &PushCommits{}
|
||||||
} else {
|
} else {
|
||||||
// if not the first commit, set the compare URL.
|
// if not the first commit, set the compare URL.
|
||||||
if opts.OldCommitID == git.EMPTY_SHA {
|
if opts.OldCommitID == git.EmptySHA {
|
||||||
isNewBranch = true
|
isNewBranch = true
|
||||||
} else {
|
} else {
|
||||||
opts.Commits.CompareURL = repo.ComposeCompareURL(opts.OldCommitID, opts.NewCommitID)
|
opts.Commits.CompareURL = repo.ComposeCompareURL(opts.OldCommitID, opts.NewCommitID)
|
||||||
|
|
|
@ -380,7 +380,7 @@ func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository) (err error
|
||||||
l.PushFront(mergeCommit)
|
l.PushFront(mergeCommit)
|
||||||
|
|
||||||
p := &api.PushPayload{
|
p := &api.PushPayload{
|
||||||
Ref: git.BRANCH_PREFIX + pr.BaseBranch,
|
Ref: git.BranchPrefix + pr.BaseBranch,
|
||||||
Before: pr.MergeBase,
|
Before: pr.MergeBase,
|
||||||
After: pr.MergedCommitID,
|
After: pr.MergedCommitID,
|
||||||
CompareURL: setting.AppURL + pr.BaseRepo.ComposeCompareURL(pr.MergeBase, pr.MergedCommitID),
|
CompareURL: setting.AppURL + pr.BaseRepo.ComposeCompareURL(pr.MergeBase, pr.MergedCommitID),
|
||||||
|
|
|
@ -158,13 +158,13 @@ func (repo *Repository) UpdateRepoFile(doer *User, opts UpdateRepoFileOptions) (
|
||||||
}
|
}
|
||||||
oldCommitID := opts.LastCommitID
|
oldCommitID := opts.LastCommitID
|
||||||
if opts.NewBranch != opts.OldBranch {
|
if opts.NewBranch != opts.OldBranch {
|
||||||
oldCommitID = git.EMPTY_SHA
|
oldCommitID = git.EmptySHA
|
||||||
}
|
}
|
||||||
if err := CommitRepoAction(CommitRepoActionOptions{
|
if err := CommitRepoAction(CommitRepoActionOptions{
|
||||||
PusherName: doer.Name,
|
PusherName: doer.Name,
|
||||||
RepoOwnerID: repo.MustOwner().ID,
|
RepoOwnerID: repo.MustOwner().ID,
|
||||||
RepoName: repo.Name,
|
RepoName: repo.Name,
|
||||||
RefFullName: git.BRANCH_PREFIX + opts.NewBranch,
|
RefFullName: git.BranchPrefix + opts.NewBranch,
|
||||||
OldCommitID: oldCommitID,
|
OldCommitID: oldCommitID,
|
||||||
NewCommitID: commit.ID.String(),
|
NewCommitID: commit.ID.String(),
|
||||||
Commits: pushCommits,
|
Commits: pushCommits,
|
||||||
|
@ -297,7 +297,7 @@ func (repo *Repository) DeleteRepoFile(doer *User, opts DeleteRepoFileOptions) (
|
||||||
PusherName: doer.Name,
|
PusherName: doer.Name,
|
||||||
RepoOwnerID: repo.MustOwner().ID,
|
RepoOwnerID: repo.MustOwner().ID,
|
||||||
RepoName: repo.Name,
|
RepoName: repo.Name,
|
||||||
RefFullName: git.BRANCH_PREFIX + opts.NewBranch,
|
RefFullName: git.BranchPrefix + opts.NewBranch,
|
||||||
OldCommitID: opts.LastCommitID,
|
OldCommitID: opts.LastCommitID,
|
||||||
NewCommitID: commit.ID.String(),
|
NewCommitID: commit.ID.String(),
|
||||||
Commits: pushCommits,
|
Commits: pushCommits,
|
||||||
|
@ -533,7 +533,7 @@ func (repo *Repository) UploadRepoFiles(doer *User, opts UploadRepoFileOptions)
|
||||||
PusherName: doer.Name,
|
PusherName: doer.Name,
|
||||||
RepoOwnerID: repo.MustOwner().ID,
|
RepoOwnerID: repo.MustOwner().ID,
|
||||||
RepoName: repo.Name,
|
RepoName: repo.Name,
|
||||||
RefFullName: git.BRANCH_PREFIX + opts.NewBranch,
|
RefFullName: git.BranchPrefix + opts.NewBranch,
|
||||||
OldCommitID: opts.LastCommitID,
|
OldCommitID: opts.LastCommitID,
|
||||||
NewCommitID: commit.ID.String(),
|
NewCommitID: commit.ID.String(),
|
||||||
Commits: pushCommits,
|
Commits: pushCommits,
|
||||||
|
|
|
@ -91,10 +91,10 @@ type PushUpdateOptions struct {
|
||||||
// PushUpdate must be called for any push actions in order to
|
// PushUpdate must be called for any push actions in order to
|
||||||
// generates necessary push action history feeds.
|
// generates necessary push action history feeds.
|
||||||
func PushUpdate(opts PushUpdateOptions) (err error) {
|
func PushUpdate(opts PushUpdateOptions) (err error) {
|
||||||
isNewRef := opts.OldCommitID == git.EMPTY_SHA
|
isNewRef := opts.OldCommitID == git.EmptySHA
|
||||||
isDelRef := opts.NewCommitID == git.EMPTY_SHA
|
isDelRef := opts.NewCommitID == git.EmptySHA
|
||||||
if isNewRef && isDelRef {
|
if isNewRef && isDelRef {
|
||||||
return fmt.Errorf("Old and new revisions are both %s", git.EMPTY_SHA)
|
return fmt.Errorf("Old and new revisions are both %s", git.EmptySHA)
|
||||||
}
|
}
|
||||||
|
|
||||||
repoPath := RepoPath(opts.RepoUserName, opts.RepoName)
|
repoPath := RepoPath(opts.RepoUserName, opts.RepoName)
|
||||||
|
@ -127,7 +127,7 @@ func PushUpdate(opts PushUpdateOptions) (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Push tags.
|
// Push tags.
|
||||||
if strings.HasPrefix(opts.RefFullName, git.TAG_PREFIX) {
|
if strings.HasPrefix(opts.RefFullName, git.TagPrefix) {
|
||||||
if err := CommitRepoAction(CommitRepoActionOptions{
|
if err := CommitRepoAction(CommitRepoActionOptions{
|
||||||
PusherName: opts.PusherName,
|
PusherName: opts.PusherName,
|
||||||
RepoOwnerID: owner.ID,
|
RepoOwnerID: owner.ID,
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -208,7 +208,7 @@ func HTTP(ctx *context.Context) {
|
||||||
RepoUserName: username,
|
RepoUserName: username,
|
||||||
RepoName: reponame,
|
RepoName: reponame,
|
||||||
}); err == nil {
|
}); err == nil {
|
||||||
go models.AddTestPullRequestTask(authUser, repo.ID, strings.TrimPrefix(refFullName, git.BRANCH_PREFIX), true)
|
go models.AddTestPullRequestTask(authUser, repo.ID, strings.TrimPrefix(refFullName, git.BranchPrefix), true)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -361,7 +361,7 @@ func TestWebhook(ctx *context.Context) {
|
||||||
if commit == nil {
|
if commit == nil {
|
||||||
ghost := models.NewGhostUser()
|
ghost := models.NewGhostUser()
|
||||||
commit = &git.Commit{
|
commit = &git.Commit{
|
||||||
ID: git.MustIDFromString(git.EMPTY_SHA),
|
ID: git.MustIDFromString(git.EmptySHA),
|
||||||
Author: ghost.NewGitSig(),
|
Author: ghost.NewGitSig(),
|
||||||
Committer: ghost.NewGitSig(),
|
Committer: ghost.NewGitSig(),
|
||||||
CommitMessage: "This is a fake commit",
|
CommitMessage: "This is a fake commit",
|
||||||
|
@ -370,7 +370,7 @@ func TestWebhook(ctx *context.Context) {
|
||||||
|
|
||||||
apiUser := ctx.User.APIFormat()
|
apiUser := ctx.User.APIFormat()
|
||||||
p := &api.PushPayload{
|
p := &api.PushPayload{
|
||||||
Ref: git.BRANCH_PREFIX + ctx.Repo.Repository.DefaultBranch,
|
Ref: git.BranchPrefix + ctx.Repo.Repository.DefaultBranch,
|
||||||
Before: commit.ID.String(),
|
Before: commit.ID.String(),
|
||||||
After: commit.ID.String(),
|
After: commit.ID.String(),
|
||||||
Commits: []*api.PayloadCommit{
|
Commits: []*api.PayloadCommit{
|
||||||
|
|
|
@ -66,7 +66,7 @@ func renderWikiPage(ctx *context.Context, isViewPage bool) (*git.Repository, str
|
||||||
}
|
}
|
||||||
pages := make([]PageMeta, 0, len(entries))
|
pages := make([]PageMeta, 0, len(entries))
|
||||||
for i := range entries {
|
for i := range entries {
|
||||||
if entries[i].Type == git.OBJECT_BLOB && strings.HasSuffix(entries[i].Name(), ".md") {
|
if entries[i].Type == git.ObjectBlob && strings.HasSuffix(entries[i].Name(), ".md") {
|
||||||
name := strings.TrimSuffix(entries[i].Name(), ".md")
|
name := strings.TrimSuffix(entries[i].Name(), ".md")
|
||||||
pages = append(pages, PageMeta{
|
pages = append(pages, PageMeta{
|
||||||
Name: name,
|
Name: name,
|
||||||
|
@ -171,7 +171,7 @@ func WikiPages(ctx *context.Context) {
|
||||||
}
|
}
|
||||||
pages := make([]PageMeta, 0, len(entries))
|
pages := make([]PageMeta, 0, len(entries))
|
||||||
for i := range entries {
|
for i := range entries {
|
||||||
if entries[i].Type == git.OBJECT_BLOB && strings.HasSuffix(entries[i].Name(), ".md") {
|
if entries[i].Type == git.ObjectBlob && strings.HasSuffix(entries[i].Name(), ".md") {
|
||||||
c, err := wikiRepo.GetCommitByPath(entries[i].Name())
|
c, err := wikiRepo.GetCommitByPath(entries[i].Name())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Handle(500, "GetCommit", err)
|
ctx.Handle(500, "GetCommit", err)
|
||||||
|
|
|
@ -2,161 +2,85 @@
|
||||||
|
|
||||||
## Introduction
|
## Introduction
|
||||||
|
|
||||||
This document explains how to contribute changes to the Gitea
|
This document explains how to contribute changes to the Gitea project. It assumes you have followed the [installation instructions](https://github.com/go-gitea/docs/tree/master/en-US/installation). Sensitive security-related issues should be reported to [security@gitea.io](mailto:security@gitea.io).
|
||||||
project. It assumes you have followed the [installation
|
|
||||||
instructions](https://github.com/go-gitea/docs/tree/master/en-US/installation)
|
|
||||||
|
|
||||||
Sensitive security-related issues should be reported to
|
|
||||||
[security@gitea.io](mailto:security@gitea.io).
|
|
||||||
|
|
||||||
## Bug reports
|
## Bug reports
|
||||||
|
|
||||||
Please search the issues on the issue tracker with a variety of keywords
|
Please search the issues on the issue tracker with a variety of keywords to ensure your bug is not already reported.
|
||||||
to ensure your bug is not already reported.
|
|
||||||
|
|
||||||
If unique, [open an issue](https://github.com/go-gitea/gitea/issues/new)
|
If unique, [open an issue](https://github.com/go-gitea/gitea/issues/new) and answer the questions so we can understand and reproduce the problematic behavior.
|
||||||
and answer the questions so we can understand and reproduce the
|
|
||||||
problematic behavior.
|
|
||||||
|
|
||||||
The burden is on you to convince us that it is actually a bug
|
The burden is on you to convince us that it is actually a bug in Gitea. This is easiest to do when you write clear, concise instructions so we can reproduce the behavior (even if it seems obvious). The more detailed and specific you are, the faster we will be able to help you. Check out [How to Report Bugs Effectively](http://www.chiark.greenend.org.uk/~sgtatham/bugs.html).
|
||||||
in Gitea. This is easiest to do when you write clear, concise
|
|
||||||
instructions so we can reproduce the behavior (even if it seems
|
|
||||||
obvious). The more detailed and specific you are, the faster
|
|
||||||
we will be able to help you. Check out [How to Report Bugs
|
|
||||||
Effectively](http://www.chiark.greenend.org.uk/~sgtatham/bugs.html).
|
|
||||||
|
|
||||||
Please be kind, remember that Gitea comes at no cost to you, and you're
|
Please be kind, remember that Gitea comes at no cost to you, and you're getting free help.
|
||||||
getting free help.
|
|
||||||
|
|
||||||
## Discuss your design
|
## Discuss your design
|
||||||
|
|
||||||
The project welcomes submissions but please let everyone know what
|
The project welcomes submissions but please let everyone know what you're working on if you want to change or add something to the Gitea repositories.
|
||||||
you're working on if you want to change or add something to the Gitea
|
|
||||||
repositories.
|
|
||||||
|
|
||||||
Before starting to write something new for the Gitea project, please
|
Before starting to write something new for the Gitea project, please [file an issue](https://github.com/go-gitea/gitea/issues/new). Significant changes must go through the [change proposal process](https://github.com/go-gitea/proposals) before they can be accepted.
|
||||||
[file an issue](https://github.com/go-gitea/gitea/issues/new).
|
|
||||||
Significant changes must go through the [change proposal
|
|
||||||
process](https://github.com/go-gitea/proposals) before they can be
|
|
||||||
accepted.
|
|
||||||
|
|
||||||
This process gives everyone a chance to validate the design, helps
|
This process gives everyone a chance to validate the design, helps prevent duplication of effort, and ensures that the idea fits inside the goals for the project and tools. It also checks that the design is sound before code is written; the code review tool is not the place for high-level discussions.
|
||||||
prevent duplication of effort, and ensures that the idea fits inside
|
|
||||||
the goals for the project and tools. It also checks that the design is
|
|
||||||
sound before code is written; the code review tool is not the place for
|
|
||||||
high-level discussions.
|
|
||||||
|
|
||||||
## Testing redux
|
## Testing redux
|
||||||
|
|
||||||
Before sending code out for review, run all the tests for the whole
|
Before sending code out for review, run all the tests for the whole tree to make sure the changes don't break other usage and keep the compatibility on upgrade. To make sure you are running the test suite exactly like we do you should install the CLI for [Drone CI](https://github.com/drone/drone), as we are using the server for continous testing, following [these instructions](http://readme.drone.io/0.5/install/cli/). After that you can simply call `drone exec` within your working directory and it will try to run the test suite locally.
|
||||||
tree to make sure the changes don't break other usage and keep the
|
|
||||||
compatibility on upgrade:
|
|
||||||
|
|
||||||
After running for a while, the command should print
|
|
||||||
|
|
||||||
```
|
|
||||||
ALL TESTS PASSED
|
|
||||||
```
|
|
||||||
|
|
||||||
## Code review
|
## Code review
|
||||||
|
|
||||||
Changes to Gitea must be reviewed before they are accepted, no matter
|
Changes to Gitea must be reviewed before they are accepted, no matter who makes the change even if it is an owner or a maintainer. We use GitHub's pull request workflow to do that and we also use [LGTM](http://lgtm.co) to ensure every PR is reviewed by at least 2 maintainers.
|
||||||
who makes the change even if an owners or a maintainer. We use github's
|
|
||||||
pull request workflow to do that and use [lgtm](http://lgtm.co) to ensure
|
Please try to make your pull request easy to review for us. Please read the "[How to get faster PR reviews](https://github.com/kubernetes/kubernetes/blob/master/docs/devel/faster_reviews.md)" guide, it has lots of useful tips for any project you may want to contribute. Some of the key points:
|
||||||
every PR is reviewed by at least 2 maintainers.
|
|
||||||
|
* Make small pull requests. The smaller, the faster to review and the more likely it will be merged soon.
|
||||||
|
* Don't make changes unrelated to your PR. Maybe there are typos on some comments, maybe refactoring would be welcome on a function... but if that is not related to your PR, please make *another* PR for that.
|
||||||
|
* Split big pull requests in multiple small ones. An incremental change will be faster to review than a huge PR.
|
||||||
|
|
||||||
## Sign your work
|
## Sign your work
|
||||||
|
|
||||||
The sign-off is a simple line at the end of the explanation for the
|
The sign-off is a simple line at the end of the explanation for the patch. Your signature certifies that you wrote the patch or otherwise have the right to pass it on as an open-source patch. The rules are pretty simple: If you can certify [DCO](DCO), then you just add a line to every git commit message:
|
||||||
patch. Your signature certifies that you wrote the patch or otherwise
|
|
||||||
have the right to pass it on as an open-source patch. The rules are
|
|
||||||
pretty simple: If you can certify [DCO](DCO), then you just add a line
|
|
||||||
to every git commit message:
|
|
||||||
|
|
||||||
```
|
```
|
||||||
Signed-off-by: Joe Smith <joe.smith@email.com>
|
Signed-off-by: Joe Smith <joe.smith@email.com>
|
||||||
```
|
```
|
||||||
|
|
||||||
Please use your real name, we really dislike pseudonyms or anonymous
|
Please use your real name, we really dislike pseudonyms or anonymous contributions. We are in the opensource world without secrets. If you set your `user.name` and `user.email` git configs, you can sign your commit automatically with `git commit -s`.
|
||||||
contributions. We are in the opensource world without secrets. If you
|
|
||||||
set your `user.name` and `user.email` git configs, you can sign your
|
|
||||||
commit automatically with `git commit -s`.
|
|
||||||
|
|
||||||
## Contributors
|
|
||||||
|
|
||||||
Everyone who sent a PR to Gitea that gets accepted will
|
|
||||||
be as a contributor. Please send a PR to add your name to
|
|
||||||
[CONTRIBUTORS](CONTRIBUTORS). For the format, see the
|
|
||||||
[CONTRIBUTORS](CONTRIBUTORS).
|
|
||||||
|
|
||||||
## Maintainers
|
## Maintainers
|
||||||
|
|
||||||
To make sure every PR have been checked, we make a team maintainers. Any
|
To make sure every PR is checked, we got team maintainers. Every PR **MUST** be reviewed by at least two maintainers (or owners) before it can get merged. A maintainer should be a contributor of Gitea (or Gogs) and contributed at least 4 accepted PRs. A contributor should apply as a maintainer in the [Gitter develop channel](https://gitter.im/go-gitea/develop). The owners or the team maintainers may invite the contributor. A maintainer should spend some time on code reviews. If a maintainer has no time to do that, they should apply to leave the maintainers team and we will give them the honor of being a member of the advisors team. Of course, if an advisor has time to code review, we will gladly welcome them back to maintainers team. If someone has no time to code review and forgets to leave the maintainers team, the owners have the power to move him from maintainers team to advisors team.
|
||||||
PR MUST be reviewed and by at least two maintainers before it can
|
|
||||||
get merged. Maintainers should be a contributor of gitea(or gogs) and
|
|
||||||
contributed at least 4 accepted PRs. And a contributor should apply as a
|
|
||||||
maintainer in [gitter Gitea develop](https://gitter.im/go-gitea/develop).
|
|
||||||
And the owners or the team maintainer could invite the contributor. A
|
|
||||||
maintainer should spend some time on code reviews. If some maintainer
|
|
||||||
have no time to do that, he should apply to leave maintainers team and
|
|
||||||
we will give him an honor to be as a member of advisor team. Of course,
|
|
||||||
if an advisor have time to code view, welcome it back to maintainers team.
|
|
||||||
If some one have no time to code view and forget to leave the maintainers,
|
|
||||||
the owners have the power to move him from maintainers team to advisors
|
|
||||||
team.
|
|
||||||
|
|
||||||
## Owners
|
## Owners
|
||||||
|
|
||||||
Since Gitea is a pure community organization without any company
|
Since Gitea is a pure community organization without any company support, to keep the development healthy we will elect the owners every year. Every time we will elect three owners. All the contributors may vote up to three people, one of which is the main owner, and the others are assistant owners. When the new owners have been elected, the old owners MUST move the power to the new ones. If an owner don't obey these rules, the others are allowed to revoke his owner status.
|
||||||
support, to keep the development healthly We will elect the owners every
|
|
||||||
year. Every time we will elect three owners. All the contributers could
|
|
||||||
vote for three owners, one is the main owner, the other two are assistant
|
|
||||||
owners. When the new owners have been elected, the old owners MUST move
|
|
||||||
the power to the new owners. If some owner don't obey these rules,
|
|
||||||
the other owners are allowed to revoke his owner status.
|
|
||||||
|
|
||||||
After the election, the new owners should say he agrees with these
|
After the election, the new owners should say they agree with these rules on the [CONTRIBUTING](CONTRIBUTING.md) on the [Gitter main channel](https://gitter.im/go-gitea/gitea). Below are the words to speak:
|
||||||
rules on the [CONTRIBUTING](CONTRIBUTING.md) on the [Gitter Gitea
|
|
||||||
Channel](https://gitter.im/go-gitea/gitea). Below is the word to speak
|
|
||||||
|
|
||||||
```
|
```
|
||||||
I'm glad to be an owner of Gitea,
|
I'm glad to be an owner of Gitea, I agree with [CONTRIBUTING](CONTRIBUTING.md). I will spend part of my time on Gitea and lead the development of Gitea.
|
||||||
I agree with [CONTRIBUTING](CONTRIBUTING.md).
|
|
||||||
I will spend part of my time on gitea
|
|
||||||
and lead the development of gitea.
|
|
||||||
```
|
```
|
||||||
|
|
||||||
For a honor to the owners, this document will add the history owners
|
To honor the past owners, here's the history of the owners and the time they served:
|
||||||
below:
|
|
||||||
|
|
||||||
2016-11-04 ~ 2017-12-31
|
* 2016-11-04 ~ 2017-12-31
|
||||||
|
* [Lunny Xiao](https://github.com/lunny) <xiaolunwen@gmail.com>
|
||||||
- lunny <xiaolunwen@gmail.com>
|
* [Thomas Boerger](https://github.com/tboerger) <thomas@webhippie.de>
|
||||||
- tboerger <thomas@webhippie.de>
|
* [Kim Carlbäcker](https://github.com/bkcsoft) <kim.carlbacker@gmail.com>
|
||||||
- bkcsoft <kim.carlbacker@gmail.com>
|
|
||||||
|
|
||||||
## Versions
|
## Versions
|
||||||
|
|
||||||
Gitea has one master as a tip branch and have many version branch
|
Gitea has the `master` branch as a tip branch and has version branches such as `v0.9`. `v0.9` is a release branch and we will tag `v0.9.0` for binary download. If `v0.9.0` has bugs, we will accept pull requests on the `v0.9` branch and publish a `v0.9.1` tag, after bringing the bug fix also to the master branch.
|
||||||
such as v0.9. v0.9 is a release branch and we will tag v0.9.0 both for
|
|
||||||
binary download. If v0.9.0 have some bugs, we will accept PR on v0.9
|
|
||||||
and publish v0.9.1 and merge bug PR to master.
|
|
||||||
|
|
||||||
Branch master is a tip version, so if you wish a production usage,
|
Since the `master` branch is a tip version, if you wish to use Gitea in production, please download the latest release tag version. All the branches will be protected via GitHub, all the PRs to every branch must be reviewed by two maintainers and must pass the automatic tests.
|
||||||
please download the latest release tag version. All the branch will be
|
|
||||||
protected via github, All the PRs to all the branches should be review
|
|
||||||
by two maintainers and pass the automatic tests.
|
|
||||||
|
|
||||||
## Copyright
|
## Copyright
|
||||||
|
|
||||||
Code that you contribute should use the standard copyright header:
|
Code that you contribute should use the standard copyright header:
|
||||||
|
|
||||||
```
|
```
|
||||||
// Copyright 2016 - 2017 The Gitea Authors. All rights reserved.
|
// Copyright 2016 The Gitea Authors. All rights reserved.
|
||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
```
|
```
|
||||||
|
|
||||||
Files in the repository are copyright the year they are added and the
|
Files in the repository contain copyright from the year they are added to the year they are last changed. If the copyright author is changed, just paste the header below the old one.
|
||||||
year they are last changed. If the copyright author is changed, just
|
|
||||||
copy the head below the old one.
|
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
Andrey Nering <nobody@nobody.tld> (@andreynering)
|
|
||||||
Kim Carlbäcker <kim.carlbacker@gmail.com> (@bkcsoft)
|
|
||||||
LefsFlare <nobody@nobody.tld> (@LefsFlarey)
|
|
||||||
Lunny Xiao <xiaolunwen@gmail.com> (@lunny)
|
|
||||||
Rachid Zarouali <nobody@nobody.tld> (@xinity)
|
|
||||||
Rémy Boulanouar <admin@dblk.org> (@DblK)
|
|
||||||
Sandro Santilli <strk@kbt.io> (@strk)
|
|
||||||
Thibault Meyer <nobody@nobody.tld> (@0xbaadf00d)
|
|
||||||
Thomas Boerger <thomas@webhippie.de> (@tboerger)
|
|
|
@ -1,4 +1,6 @@
|
||||||
Andrey Nering <nobody@nobody.tld> (@andreynering)
|
Alexey Makhov <amakhov@avito.ru> (@makhov)
|
||||||
|
Andrey Nering <andrey.nering@gmail.com> (@andreynering)
|
||||||
|
Kees de Vries <bouwko@gmail.com> (@Bwko)
|
||||||
Kim Carlbäcker <kim.carlbacker@gmail.com> (@bkcsoft)
|
Kim Carlbäcker <kim.carlbacker@gmail.com> (@bkcsoft)
|
||||||
LefsFlare <nobody@nobody.tld> (@LefsFlarey)
|
LefsFlare <nobody@nobody.tld> (@LefsFlarey)
|
||||||
Lunny Xiao <xiaolunwen@gmail.com> (@lunny)
|
Lunny Xiao <xiaolunwen@gmail.com> (@lunny)
|
||||||
|
@ -6,5 +8,5 @@ Matthias Loibl <mail@matthiasloibl.com> (@metalmatze)
|
||||||
Rachid Zarouali <nobody@nobody.tld> (@xinity)
|
Rachid Zarouali <nobody@nobody.tld> (@xinity)
|
||||||
Rémy Boulanouar <admin@dblk.org> (@DblK)
|
Rémy Boulanouar <admin@dblk.org> (@DblK)
|
||||||
Sandro Santilli <strk@kbt.io> (@strk)
|
Sandro Santilli <strk@kbt.io> (@strk)
|
||||||
Thibault Meyer <nobody@nobody.tld> (@0xbaadf00d)
|
Thibault Meyer <meyer.thibault@gmail.com> (@0xbaadf00d)
|
||||||
Thomas Boerger <thomas@webhippie.de> (@tboerger)
|
Thomas Boerger <thomas@webhippie.de> (@tboerger)
|
||||||
|
|
|
@ -10,6 +10,15 @@
|
||||||
This project is a Go module to access Git through shell commands. For further
|
This project is a Go module to access Git through shell commands. For further
|
||||||
informations take a look at the current [documentation](https://godoc.org/code.gitea.io/git).
|
informations take a look at the current [documentation](https://godoc.org/code.gitea.io/git).
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
Fork -> Patch -> Push -> Pull Request
|
||||||
|
|
||||||
|
## Authors
|
||||||
|
|
||||||
|
* [Maintainers](https://github.com/orgs/go-gitea/people)
|
||||||
|
* [Contributors](https://github.com/go-gitea/git/graphs/contributors)
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
This project is under the MIT License. See the [LICENSE](LICENSE) file for the
|
This project is under the MIT License. See the [LICENSE](LICENSE) file for the
|
||||||
|
|
|
@ -25,6 +25,7 @@ func (b *Blob) Data() (io.Reader, error) {
|
||||||
return bytes.NewBuffer(stdout), nil
|
return bytes.NewBuffer(stdout), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DataPipeline gets content of blob and write the result or error to stdout or stderr
|
||||||
func (b *Blob) DataPipeline(stdout, stderr io.Writer) error {
|
func (b *Blob) DataPipeline(stdout, stderr io.Writer) error {
|
||||||
return NewCommand("show", b.ID.String()).RunInDirPipeline(b.repo.Path, stdout, stderr)
|
return NewCommand("show", b.ID.String()).RunInDirPipeline(b.repo.Path, stdout, stderr)
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,11 @@ import (
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// GlobalCommandArgs global command args for external package setting
|
||||||
|
GlobalCommandArgs []string
|
||||||
|
)
|
||||||
|
|
||||||
// Command represents a command with its subcommands or arguments.
|
// Command represents a command with its subcommands or arguments.
|
||||||
type Command struct {
|
type Command struct {
|
||||||
name string
|
name string
|
||||||
|
@ -30,7 +35,7 @@ func (c *Command) String() string {
|
||||||
func NewCommand(args ...string) *Command {
|
func NewCommand(args ...string) *Command {
|
||||||
return &Command{
|
return &Command{
|
||||||
name: "git",
|
name: "git",
|
||||||
args: args,
|
args: append(GlobalCommandArgs, args...),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,13 +45,11 @@ func (c *Command) AddArguments(args ...string) *Command {
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
const DEFAULT_TIMEOUT = 60 * time.Second
|
|
||||||
|
|
||||||
// RunInDirTimeoutPipeline executes the command in given directory with given timeout,
|
// RunInDirTimeoutPipeline executes the command in given directory with given timeout,
|
||||||
// it pipes stdout and stderr to given io.Writer.
|
// it pipes stdout and stderr to given io.Writer.
|
||||||
func (c *Command) RunInDirTimeoutPipeline(timeout time.Duration, dir string, stdout, stderr io.Writer) error {
|
func (c *Command) RunInDirTimeoutPipeline(timeout time.Duration, dir string, stdout, stderr io.Writer) error {
|
||||||
if timeout == -1 {
|
if timeout == -1 {
|
||||||
timeout = DEFAULT_TIMEOUT
|
timeout = 60 * time.Second
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(dir) == 0 {
|
if len(dir) == 0 {
|
||||||
|
@ -106,7 +109,7 @@ func (c *Command) RunInDirPipeline(dir string, stdout, stderr io.Writer) error {
|
||||||
return c.RunInDirTimeoutPipeline(-1, dir, stdout, stderr)
|
return c.RunInDirTimeoutPipeline(-1, dir, stdout, stderr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// RunInDir executes the command in given directory
|
// RunInDirBytes executes the command in given directory
|
||||||
// and returns stdout in []byte and error (combined with stderr).
|
// and returns stdout in []byte and error (combined with stderr).
|
||||||
func (c *Command) RunInDirBytes(dir string) ([]byte, error) {
|
func (c *Command) RunInDirBytes(dir string) ([]byte, error) {
|
||||||
return c.RunInDirTimeout(-1, dir)
|
return c.RunInDirTimeout(-1, dir)
|
||||||
|
|
|
@ -18,13 +18,13 @@ import (
|
||||||
// Commit represents a git commit.
|
// Commit represents a git commit.
|
||||||
type Commit struct {
|
type Commit struct {
|
||||||
Tree
|
Tree
|
||||||
ID sha1 // The ID of this commit object
|
ID SHA1 // The ID of this commit object
|
||||||
Author *Signature
|
Author *Signature
|
||||||
Committer *Signature
|
Committer *Signature
|
||||||
CommitMessage string
|
CommitMessage string
|
||||||
|
|
||||||
parents []sha1 // SHA1 strings
|
parents []SHA1 // SHA1 strings
|
||||||
submoduleCache *objectCache
|
submoduleCache *ObjectCache
|
||||||
}
|
}
|
||||||
|
|
||||||
// Message returns the commit message. Same as retrieving CommitMessage directly.
|
// Message returns the commit message. Same as retrieving CommitMessage directly.
|
||||||
|
@ -39,9 +39,9 @@ func (c *Commit) Summary() string {
|
||||||
|
|
||||||
// ParentID returns oid of n-th parent (0-based index).
|
// ParentID returns oid of n-th parent (0-based index).
|
||||||
// It returns nil if no such parent exists.
|
// It returns nil if no such parent exists.
|
||||||
func (c *Commit) ParentID(n int) (sha1, error) {
|
func (c *Commit) ParentID(n int) (SHA1, error) {
|
||||||
if n >= len(c.parents) {
|
if n >= len(c.parents) {
|
||||||
return sha1{}, ErrNotExist{"", ""}
|
return SHA1{}, ErrNotExist{"", ""}
|
||||||
}
|
}
|
||||||
return c.parents[n], nil
|
return c.parents[n], nil
|
||||||
}
|
}
|
||||||
|
@ -73,6 +73,7 @@ func isImageFile(data []byte) (string, bool) {
|
||||||
return contentType, false
|
return contentType, false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsImageFile is a file image type
|
||||||
func (c *Commit) IsImageFile(name string) bool {
|
func (c *Commit) IsImageFile(name string) bool {
|
||||||
blob, err := c.GetBlobByPath(name)
|
blob, err := c.GetBlobByPath(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -95,7 +96,7 @@ func (c *Commit) GetCommitByPath(relpath string) (*Commit, error) {
|
||||||
return c.repo.getCommitByPathWithID(c.ID, relpath)
|
return c.repo.getCommitByPathWithID(c.ID, relpath)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddAllChanges marks local changes to be ready for commit.
|
// AddChanges marks local changes to be ready for commit.
|
||||||
func AddChanges(repoPath string, all bool, files ...string) error {
|
func AddChanges(repoPath string, all bool, files ...string) error {
|
||||||
cmd := NewCommand("add")
|
cmd := NewCommand("add")
|
||||||
if all {
|
if all {
|
||||||
|
@ -105,6 +106,7 @@ func AddChanges(repoPath string, all bool, files ...string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CommitChangesOptions the options when a commit created
|
||||||
type CommitChangesOptions struct {
|
type CommitChangesOptions struct {
|
||||||
Committer *Signature
|
Committer *Signature
|
||||||
Author *Signature
|
Author *Signature
|
||||||
|
@ -166,22 +168,27 @@ func CommitsCount(repoPath, revision string) (int64, error) {
|
||||||
return commitsCount(repoPath, revision, "")
|
return commitsCount(repoPath, revision, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CommitsCount returns number of total commits of until current revision.
|
||||||
func (c *Commit) CommitsCount() (int64, error) {
|
func (c *Commit) CommitsCount() (int64, error) {
|
||||||
return CommitsCount(c.repo.Path, c.ID.String())
|
return CommitsCount(c.repo.Path, c.ID.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CommitsByRange returns the specific page commits before current revision, every page's number default by CommitsRangeSize
|
||||||
func (c *Commit) CommitsByRange(page int) (*list.List, error) {
|
func (c *Commit) CommitsByRange(page int) (*list.List, error) {
|
||||||
return c.repo.commitsByRange(c.ID, page)
|
return c.repo.commitsByRange(c.ID, page)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CommitsBefore returns all the commits before current revision
|
||||||
func (c *Commit) CommitsBefore() (*list.List, error) {
|
func (c *Commit) CommitsBefore() (*list.List, error) {
|
||||||
return c.repo.getCommitsBefore(c.ID)
|
return c.repo.getCommitsBefore(c.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CommitsBeforeLimit returns num commits before current revision
|
||||||
func (c *Commit) CommitsBeforeLimit(num int) (*list.List, error) {
|
func (c *Commit) CommitsBeforeLimit(num int) (*list.List, error) {
|
||||||
return c.repo.getCommitsBeforeLimit(c.ID, num)
|
return c.repo.getCommitsBeforeLimit(c.ID, num)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CommitsBeforeUntil returns the commits between commitID to current revision
|
||||||
func (c *Commit) CommitsBeforeUntil(commitID string) (*list.List, error) {
|
func (c *Commit) CommitsBeforeUntil(commitID string) (*list.List, error) {
|
||||||
endCommit, err := c.repo.GetCommit(commitID)
|
endCommit, err := c.repo.GetCommit(commitID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -190,15 +197,18 @@ func (c *Commit) CommitsBeforeUntil(commitID string) (*list.List, error) {
|
||||||
return c.repo.CommitsBetween(c, endCommit)
|
return c.repo.CommitsBetween(c, endCommit)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SearchCommits returns the commits match the keyword before current revision
|
||||||
func (c *Commit) SearchCommits(keyword string) (*list.List, error) {
|
func (c *Commit) SearchCommits(keyword string) (*list.List, error) {
|
||||||
return c.repo.searchCommits(c.ID, keyword)
|
return c.repo.searchCommits(c.ID, keyword)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetFilesChangedSinceCommit get all changed file names between pastCommit to current revision
|
||||||
func (c *Commit) GetFilesChangedSinceCommit(pastCommit string) ([]string, error) {
|
func (c *Commit) GetFilesChangedSinceCommit(pastCommit string) ([]string, error) {
|
||||||
return c.repo.getFilesChanged(pastCommit, c.ID.String())
|
return c.repo.getFilesChanged(pastCommit, c.ID.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Commit) GetSubModules() (*objectCache, error) {
|
// GetSubModules get all the sub modules of current revision git tree
|
||||||
|
func (c *Commit) GetSubModules() (*ObjectCache, error) {
|
||||||
if c.submoduleCache != nil {
|
if c.submoduleCache != nil {
|
||||||
return c.submoduleCache, nil
|
return c.submoduleCache, nil
|
||||||
}
|
}
|
||||||
|
@ -236,6 +246,7 @@ func (c *Commit) GetSubModules() (*objectCache, error) {
|
||||||
return c.submoduleCache, nil
|
return c.submoduleCache, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetSubModule get the sub module according entryname
|
||||||
func (c *Commit) GetSubModule(entryname string) (*SubModule, error) {
|
func (c *Commit) GetSubModule(entryname string) (*SubModule, error) {
|
||||||
modules, err := c.GetSubModules()
|
modules, err := c.GetSubModules()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -10,13 +10,17 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ArchiveType archive types
|
||||||
type ArchiveType int
|
type ArchiveType int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
// ZIP zip archive type
|
||||||
ZIP ArchiveType = iota + 1
|
ZIP ArchiveType = iota + 1
|
||||||
|
// TARGZ tar gz archive type
|
||||||
TARGZ
|
TARGZ
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// CreateArchive create archive content to the target path
|
||||||
func (c *Commit) CreateArchive(target string, archiveType ArchiveType) error {
|
func (c *Commit) CreateArchive(target string, archiveType ArchiveType) error {
|
||||||
var format string
|
var format string
|
||||||
switch archiveType {
|
switch archiveType {
|
||||||
|
|
|
@ -9,10 +9,12 @@ import (
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ErrExecTimeout error when exec timed out
|
||||||
type ErrExecTimeout struct {
|
type ErrExecTimeout struct {
|
||||||
Duration time.Duration
|
Duration time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsErrExecTimeout if some error is ErrExecTimeout
|
||||||
func IsErrExecTimeout(err error) bool {
|
func IsErrExecTimeout(err error) bool {
|
||||||
_, ok := err.(ErrExecTimeout)
|
_, ok := err.(ErrExecTimeout)
|
||||||
return ok
|
return ok
|
||||||
|
@ -22,11 +24,13 @@ func (err ErrExecTimeout) Error() string {
|
||||||
return fmt.Sprintf("execution is timeout [duration: %v]", err.Duration)
|
return fmt.Sprintf("execution is timeout [duration: %v]", err.Duration)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ErrNotExist commit not exist error
|
||||||
type ErrNotExist struct {
|
type ErrNotExist struct {
|
||||||
ID string
|
ID string
|
||||||
RelPath string
|
RelPath string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsErrNotExist if some error is ErrNotExist
|
||||||
func IsErrNotExist(err error) bool {
|
func IsErrNotExist(err error) bool {
|
||||||
_, ok := err.(ErrNotExist)
|
_, ok := err.(ErrNotExist)
|
||||||
return ok
|
return ok
|
||||||
|
@ -36,10 +40,12 @@ func (err ErrNotExist) Error() string {
|
||||||
return fmt.Sprintf("object does not exist [id: %s, rel_path: %s]", err.ID, err.RelPath)
|
return fmt.Sprintf("object does not exist [id: %s, rel_path: %s]", err.ID, err.RelPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ErrUnsupportedVersion error when required git version not matched
|
||||||
type ErrUnsupportedVersion struct {
|
type ErrUnsupportedVersion struct {
|
||||||
Required string
|
Required string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsErrUnsupportedVersion if some error is ErrUnsupportedVersion
|
||||||
func IsErrUnsupportedVersion(err error) bool {
|
func IsErrUnsupportedVersion(err error) bool {
|
||||||
_, ok := err.(ErrUnsupportedVersion)
|
_, ok := err.(ErrUnsupportedVersion)
|
||||||
return ok
|
return ok
|
||||||
|
|
|
@ -10,16 +10,16 @@ import (
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
const _VERSION = "0.4.2"
|
// Version return this package's current version
|
||||||
|
|
||||||
func Version() string {
|
func Version() string {
|
||||||
return _VERSION
|
return "0.4.2"
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// Debug enables verbose logging on everything.
|
// Debug enables verbose logging on everything.
|
||||||
// This should be false in case Gogs starts in SSH mode.
|
// This should be false in case Gogs starts in SSH mode.
|
||||||
Debug = false
|
Debug = false
|
||||||
|
// Prefix the log prefix
|
||||||
Prefix = "[git-module] "
|
Prefix = "[git-module] "
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ func log(format string, args ...interface{}) {
|
||||||
|
|
||||||
var gitVersion string
|
var gitVersion string
|
||||||
|
|
||||||
// Version returns current Git version from shell.
|
// BinVersion returns current Git version from shell.
|
||||||
func BinVersion() (string, error) {
|
func BinVersion() (string, error) {
|
||||||
if len(gitVersion) > 0 {
|
if len(gitVersion) > 0 {
|
||||||
return gitVersion, nil
|
return gitVersion, nil
|
||||||
|
|
|
@ -22,6 +22,7 @@ var hookNames = []string{
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
// ErrNotValidHook error when a git hook is not valid
|
||||||
ErrNotValidHook = errors.New("not a valid Git hook")
|
ErrNotValidHook = errors.New("not a valid Git hook")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -70,6 +71,7 @@ func GetHook(repoPath, name string) (*Hook, error) {
|
||||||
return h, nil
|
return h, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Name return the name of the hook
|
||||||
func (h *Hook) Name() string {
|
func (h *Hook) Name() string {
|
||||||
return h.name
|
return h.name
|
||||||
}
|
}
|
||||||
|
@ -102,6 +104,7 @@ func ListHooks(repoPath string) (_ []*Hook, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
// HookPathUpdate hook update path
|
||||||
HookPathUpdate = "hooks/update"
|
HookPathUpdate = "hooks/update"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -18,11 +18,11 @@ import (
|
||||||
type Repository struct {
|
type Repository struct {
|
||||||
Path string
|
Path string
|
||||||
|
|
||||||
commitCache *objectCache
|
commitCache *ObjectCache
|
||||||
tagCache *objectCache
|
tagCache *ObjectCache
|
||||||
}
|
}
|
||||||
|
|
||||||
const _PRETTY_LOG_FORMAT = `--pretty=format:%H`
|
const prettyLogFormat = `--pretty=format:%H`
|
||||||
|
|
||||||
func (repo *Repository) parsePrettyFormatLogToList(logs []byte) (*list.List, error) {
|
func (repo *Repository) parsePrettyFormatLogToList(logs []byte) (*list.List, error) {
|
||||||
l := list.New()
|
l := list.New()
|
||||||
|
@ -32,8 +32,8 @@ func (repo *Repository) parsePrettyFormatLogToList(logs []byte) (*list.List, err
|
||||||
|
|
||||||
parts := bytes.Split(logs, []byte{'\n'})
|
parts := bytes.Split(logs, []byte{'\n'})
|
||||||
|
|
||||||
for _, commitId := range parts {
|
for _, commitID := range parts {
|
||||||
commit, err := repo.GetCommit(string(commitId))
|
commit, err := repo.GetCommit(string(commitID))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -80,6 +80,7 @@ func OpenRepository(repoPath string) (*Repository, error) {
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CloneRepoOptions options when clone a repository
|
||||||
type CloneRepoOptions struct {
|
type CloneRepoOptions struct {
|
||||||
Timeout time.Duration
|
Timeout time.Duration
|
||||||
Mirror bool
|
Mirror bool
|
||||||
|
@ -118,6 +119,7 @@ func Clone(from, to string, opts CloneRepoOptions) (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PullRemoteOptions options when pull from remote
|
||||||
type PullRemoteOptions struct {
|
type PullRemoteOptions struct {
|
||||||
Timeout time.Duration
|
Timeout time.Duration
|
||||||
All bool
|
All bool
|
||||||
|
@ -153,6 +155,7 @@ func Push(repoPath, remote, branch string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CheckoutOptions options when heck out some branch
|
||||||
type CheckoutOptions struct {
|
type CheckoutOptions struct {
|
||||||
Timeout time.Duration
|
Timeout time.Duration
|
||||||
Branch string
|
Branch string
|
||||||
|
|
|
@ -11,7 +11,8 @@ import (
|
||||||
"github.com/mcuadros/go-version"
|
"github.com/mcuadros/go-version"
|
||||||
)
|
)
|
||||||
|
|
||||||
const BRANCH_PREFIX = "refs/heads/"
|
// BranchPrefix base dir of the branch information file store on git
|
||||||
|
const BranchPrefix = "refs/heads/"
|
||||||
|
|
||||||
// IsReferenceExist returns true if given reference exists in the repository.
|
// IsReferenceExist returns true if given reference exists in the repository.
|
||||||
func IsReferenceExist(repoPath, name string) bool {
|
func IsReferenceExist(repoPath, name string) bool {
|
||||||
|
@ -21,9 +22,10 @@ func IsReferenceExist(repoPath, name string) bool {
|
||||||
|
|
||||||
// IsBranchExist returns true if given branch exists in the repository.
|
// IsBranchExist returns true if given branch exists in the repository.
|
||||||
func IsBranchExist(repoPath, name string) bool {
|
func IsBranchExist(repoPath, name string) bool {
|
||||||
return IsReferenceExist(repoPath, BRANCH_PREFIX+name)
|
return IsReferenceExist(repoPath, BranchPrefix+name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsBranchExist returns true if given branch exists in current repository.
|
||||||
func (repo *Repository) IsBranchExist(name string) bool {
|
func (repo *Repository) IsBranchExist(name string) bool {
|
||||||
return IsBranchExist(repo.Path, name)
|
return IsBranchExist(repo.Path, name)
|
||||||
}
|
}
|
||||||
|
@ -42,12 +44,12 @@ func (repo *Repository) GetHEADBranch() (*Branch, error) {
|
||||||
}
|
}
|
||||||
stdout = strings.TrimSpace(stdout)
|
stdout = strings.TrimSpace(stdout)
|
||||||
|
|
||||||
if !strings.HasPrefix(stdout, BRANCH_PREFIX) {
|
if !strings.HasPrefix(stdout, BranchPrefix) {
|
||||||
return nil, fmt.Errorf("invalid HEAD branch: %v", stdout)
|
return nil, fmt.Errorf("invalid HEAD branch: %v", stdout)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &Branch{
|
return &Branch{
|
||||||
Name: stdout[len(BRANCH_PREFIX):],
|
Name: stdout[len(BranchPrefix):],
|
||||||
Path: stdout,
|
Path: stdout,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
@ -58,7 +60,7 @@ func (repo *Repository) SetDefaultBranch(name string) error {
|
||||||
return ErrUnsupportedVersion{"1.7.10"}
|
return ErrUnsupportedVersion{"1.7.10"}
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := NewCommand("symbolic-ref", "HEAD", BRANCH_PREFIX+name).RunInDir(repo.Path)
|
_, err := NewCommand("symbolic-ref", "HEAD", BranchPrefix+name).RunInDir(repo.Path)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,12 +78,12 @@ func (repo *Repository) GetBranches() ([]string, error) {
|
||||||
if len(fields) != 2 {
|
if len(fields) != 2 {
|
||||||
continue // NOTE: I should believe git will not give me wrong string.
|
continue // NOTE: I should believe git will not give me wrong string.
|
||||||
}
|
}
|
||||||
branches[i] = strings.TrimPrefix(fields[1], BRANCH_PREFIX)
|
branches[i] = strings.TrimPrefix(fields[1], BranchPrefix)
|
||||||
}
|
}
|
||||||
return branches, nil
|
return branches, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Option(s) for delete branch
|
// DeleteBranchOptions Option(s) for delete branch
|
||||||
type DeleteBranchOptions struct {
|
type DeleteBranchOptions struct {
|
||||||
Force bool
|
Force bool
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,12 +28,12 @@ func (repo *Repository) getRefCommitID(name string) (string, error) {
|
||||||
|
|
||||||
// GetBranchCommitID returns last commit ID string of given branch.
|
// GetBranchCommitID returns last commit ID string of given branch.
|
||||||
func (repo *Repository) GetBranchCommitID(name string) (string, error) {
|
func (repo *Repository) GetBranchCommitID(name string) (string, error) {
|
||||||
return repo.getRefCommitID(BRANCH_PREFIX + name)
|
return repo.getRefCommitID(BranchPrefix + name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTagCommitID returns last commit ID string of given tag.
|
// GetTagCommitID returns last commit ID string of given tag.
|
||||||
func (repo *Repository) GetTagCommitID(name string) (string, error) {
|
func (repo *Repository) GetTagCommitID(name string) (string, error) {
|
||||||
return repo.getRefCommitID(TAG_PREFIX + name)
|
return repo.getRefCommitID(TagPrefix + name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseCommitData parses commit information from the (uncompressed) raw
|
// parseCommitData parses commit information from the (uncompressed) raw
|
||||||
|
@ -41,7 +41,7 @@ func (repo *Repository) GetTagCommitID(name string) (string, error) {
|
||||||
// \n\n separate headers from message
|
// \n\n separate headers from message
|
||||||
func parseCommitData(data []byte) (*Commit, error) {
|
func parseCommitData(data []byte) (*Commit, error) {
|
||||||
commit := new(Commit)
|
commit := new(Commit)
|
||||||
commit.parents = make([]sha1, 0, 1)
|
commit.parents = make([]SHA1, 0, 1)
|
||||||
// we now have the contents of the commit object. Let's investigate...
|
// we now have the contents of the commit object. Let's investigate...
|
||||||
nextline := 0
|
nextline := 0
|
||||||
l:
|
l:
|
||||||
|
@ -90,7 +90,7 @@ l:
|
||||||
return commit, nil
|
return commit, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (repo *Repository) getCommit(id sha1) (*Commit, error) {
|
func (repo *Repository) getCommit(id SHA1) (*Commit, error) {
|
||||||
c, ok := repo.commitCache.Get(id.String())
|
c, ok := repo.commitCache.Get(id.String())
|
||||||
if ok {
|
if ok {
|
||||||
log("Hit cache: %s", id)
|
log("Hit cache: %s", id)
|
||||||
|
@ -142,6 +142,7 @@ func (repo *Repository) GetBranchCommit(name string) (*Commit, error) {
|
||||||
return repo.GetCommit(commitID)
|
return repo.GetCommit(commitID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetTagCommit get the commit of the specific tag via name
|
||||||
func (repo *Repository) GetTagCommit(name string) (*Commit, error) {
|
func (repo *Repository) GetTagCommit(name string) (*Commit, error) {
|
||||||
commitID, err := repo.GetTagCommitID(name)
|
commitID, err := repo.GetTagCommitID(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -150,13 +151,13 @@ func (repo *Repository) GetTagCommit(name string) (*Commit, error) {
|
||||||
return repo.GetCommit(commitID)
|
return repo.GetCommit(commitID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (repo *Repository) getCommitByPathWithID(id sha1, relpath string) (*Commit, error) {
|
func (repo *Repository) getCommitByPathWithID(id SHA1, relpath string) (*Commit, error) {
|
||||||
// File name starts with ':' must be escaped.
|
// File name starts with ':' must be escaped.
|
||||||
if relpath[0] == ':' {
|
if relpath[0] == ':' {
|
||||||
relpath = `\` + relpath
|
relpath = `\` + relpath
|
||||||
}
|
}
|
||||||
|
|
||||||
stdout, err := NewCommand("log", "-1", _PRETTY_LOG_FORMAT, id.String(), "--", relpath).RunInDir(repo.Path)
|
stdout, err := NewCommand("log", "-1", prettyLogFormat, id.String(), "--", relpath).RunInDir(repo.Path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -171,7 +172,7 @@ func (repo *Repository) getCommitByPathWithID(id sha1, relpath string) (*Commit,
|
||||||
|
|
||||||
// GetCommitByPath returns the last commit of relative path.
|
// GetCommitByPath returns the last commit of relative path.
|
||||||
func (repo *Repository) GetCommitByPath(relpath string) (*Commit, error) {
|
func (repo *Repository) GetCommitByPath(relpath string) (*Commit, error) {
|
||||||
stdout, err := NewCommand("log", "-1", _PRETTY_LOG_FORMAT, "--", relpath).RunInDirBytes(repo.Path)
|
stdout, err := NewCommand("log", "-1", prettyLogFormat, "--", relpath).RunInDirBytes(repo.Path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -183,19 +184,20 @@ func (repo *Repository) GetCommitByPath(relpath string) (*Commit, error) {
|
||||||
return commits.Front().Value.(*Commit), nil
|
return commits.Front().Value.(*Commit), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CommitsRangeSize the default commits range size
|
||||||
var CommitsRangeSize = 50
|
var CommitsRangeSize = 50
|
||||||
|
|
||||||
func (repo *Repository) commitsByRange(id sha1, page int) (*list.List, error) {
|
func (repo *Repository) commitsByRange(id SHA1, page int) (*list.List, error) {
|
||||||
stdout, err := NewCommand("log", id.String(), "--skip="+strconv.Itoa((page-1)*CommitsRangeSize),
|
stdout, err := NewCommand("log", id.String(), "--skip="+strconv.Itoa((page-1)*CommitsRangeSize),
|
||||||
"--max-count="+strconv.Itoa(CommitsRangeSize), _PRETTY_LOG_FORMAT).RunInDirBytes(repo.Path)
|
"--max-count="+strconv.Itoa(CommitsRangeSize), prettyLogFormat).RunInDirBytes(repo.Path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return repo.parsePrettyFormatLogToList(stdout)
|
return repo.parsePrettyFormatLogToList(stdout)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (repo *Repository) searchCommits(id sha1, keyword string) (*list.List, error) {
|
func (repo *Repository) searchCommits(id SHA1, keyword string) (*list.List, error) {
|
||||||
stdout, err := NewCommand("log", id.String(), "-100", "-i", "--grep="+keyword, _PRETTY_LOG_FORMAT).RunInDirBytes(repo.Path)
|
stdout, err := NewCommand("log", id.String(), "-100", "-i", "--grep="+keyword, prettyLogFormat).RunInDirBytes(repo.Path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -210,19 +212,22 @@ func (repo *Repository) getFilesChanged(id1 string, id2 string) ([]string, error
|
||||||
return strings.Split(string(stdout), "\n"), nil
|
return strings.Split(string(stdout), "\n"), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FileCommitsCount return the number of files at a revison
|
||||||
func (repo *Repository) FileCommitsCount(revision, file string) (int64, error) {
|
func (repo *Repository) FileCommitsCount(revision, file string) (int64, error) {
|
||||||
return commitsCount(repo.Path, revision, file)
|
return commitsCount(repo.Path, revision, file)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CommitsByFileAndRange return the commits accroding revison file and the page
|
||||||
func (repo *Repository) CommitsByFileAndRange(revision, file string, page int) (*list.List, error) {
|
func (repo *Repository) CommitsByFileAndRange(revision, file string, page int) (*list.List, error) {
|
||||||
stdout, err := NewCommand("log", revision, "--skip="+strconv.Itoa((page-1)*50),
|
stdout, err := NewCommand("log", revision, "--skip="+strconv.Itoa((page-1)*50),
|
||||||
"--max-count="+strconv.Itoa(CommitsRangeSize), _PRETTY_LOG_FORMAT, "--", file).RunInDirBytes(repo.Path)
|
"--max-count="+strconv.Itoa(CommitsRangeSize), prettyLogFormat, "--", file).RunInDirBytes(repo.Path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return repo.parsePrettyFormatLogToList(stdout)
|
return repo.parsePrettyFormatLogToList(stdout)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FilesCountBetween return the number of files changed between two commits
|
||||||
func (repo *Repository) FilesCountBetween(startCommitID, endCommitID string) (int, error) {
|
func (repo *Repository) FilesCountBetween(startCommitID, endCommitID string) (int, error) {
|
||||||
stdout, err := NewCommand("diff", "--name-only", startCommitID+"..."+endCommitID).RunInDir(repo.Path)
|
stdout, err := NewCommand("diff", "--name-only", startCommitID+"..."+endCommitID).RunInDir(repo.Path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -266,6 +271,7 @@ func (repo *Repository) CommitsBetween(last *Commit, before *Commit) (*list.List
|
||||||
return l, nil
|
return l, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CommitsBetweenIDs return commits between twoe commits
|
||||||
func (repo *Repository) CommitsBetweenIDs(last, before string) (*list.List, error) {
|
func (repo *Repository) CommitsBetweenIDs(last, before string) (*list.List, error) {
|
||||||
lastCommit, err := repo.GetCommit(last)
|
lastCommit, err := repo.GetCommit(last)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -278,12 +284,13 @@ func (repo *Repository) CommitsBetweenIDs(last, before string) (*list.List, erro
|
||||||
return repo.CommitsBetween(lastCommit, beforeCommit)
|
return repo.CommitsBetween(lastCommit, beforeCommit)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CommitsCountBetween return numbers of commits between two commits
|
||||||
func (repo *Repository) CommitsCountBetween(start, end string) (int64, error) {
|
func (repo *Repository) CommitsCountBetween(start, end string) (int64, error) {
|
||||||
return commitsCount(repo.Path, start+"..."+end, "")
|
return commitsCount(repo.Path, start+"..."+end, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
// The limit is depth, not total number of returned commits.
|
// commitsBefore the limit is depth, not total number of returned commits.
|
||||||
func (repo *Repository) commitsBefore(l *list.List, parent *list.Element, id sha1, current, limit int) error {
|
func (repo *Repository) commitsBefore(l *list.List, parent *list.Element, id SHA1, current, limit int) error {
|
||||||
// Reach the limit
|
// Reach the limit
|
||||||
if limit > 0 && current > limit {
|
if limit > 0 && current > limit {
|
||||||
return nil
|
return nil
|
||||||
|
@ -342,12 +349,12 @@ func (repo *Repository) commitsBefore(l *list.List, parent *list.Element, id sha
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (repo *Repository) getCommitsBefore(id sha1) (*list.List, error) {
|
func (repo *Repository) getCommitsBefore(id SHA1) (*list.List, error) {
|
||||||
l := list.New()
|
l := list.New()
|
||||||
return l, repo.commitsBefore(l, nil, id, 1, 0)
|
return l, repo.commitsBefore(l, nil, id, 1, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (repo *Repository) getCommitsBeforeLimit(id sha1, num int) (*list.List, error) {
|
func (repo *Repository) getCommitsBeforeLimit(id SHA1, num int) (*list.List, error) {
|
||||||
l := list.New()
|
l := list.New()
|
||||||
return l, repo.commitsBefore(l, nil, id, 1, num)
|
return l, repo.commitsBefore(l, nil, id, 1, num)
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,10 +4,12 @@
|
||||||
|
|
||||||
package git
|
package git
|
||||||
|
|
||||||
|
// GetHook get one hook accroding the name on a repository
|
||||||
func (repo *Repository) GetHook(name string) (*Hook, error) {
|
func (repo *Repository) GetHook(name string) (*Hook, error) {
|
||||||
return GetHook(repo.Path, name)
|
return GetHook(repo.Path, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Hooks get all the hooks on the repository
|
||||||
func (repo *Repository) Hooks() ([]*Hook, error) {
|
func (repo *Repository) Hooks() ([]*Hook, error) {
|
||||||
return ListHooks(repo.Path)
|
return ListHooks(repo.Path)
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,11 +4,16 @@
|
||||||
|
|
||||||
package git
|
package git
|
||||||
|
|
||||||
|
// ObjectType git object type
|
||||||
type ObjectType string
|
type ObjectType string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
OBJECT_COMMIT ObjectType = "commit"
|
// ObjectCommit commit object type
|
||||||
OBJECT_TREE ObjectType = "tree"
|
ObjectCommit ObjectType = "commit"
|
||||||
OBJECT_BLOB ObjectType = "blob"
|
// ObjectTree tree object type
|
||||||
OBJECT_TAG ObjectType = "tag"
|
ObjectTree ObjectType = "tree"
|
||||||
|
// ObjectBlob blob object type
|
||||||
|
ObjectBlob ObjectType = "blob"
|
||||||
|
// ObjectTag tag object type
|
||||||
|
ObjectTag ObjectType = "tag"
|
||||||
)
|
)
|
||||||
|
|
|
@ -50,7 +50,7 @@ func (repo *Repository) GetPullRequestInfo(basePath, baseBranch, headBranch stri
|
||||||
return nil, fmt.Errorf("GetMergeBase: %v", err)
|
return nil, fmt.Errorf("GetMergeBase: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
logs, err := NewCommand("log", prInfo.MergeBase+"..."+headBranch, _PRETTY_LOG_FORMAT).RunInDirBytes(repo.Path)
|
logs, err := NewCommand("log", prInfo.MergeBase+"..."+headBranch, prettyLogFormat).RunInDirBytes(repo.Path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,23 +10,26 @@ import (
|
||||||
"github.com/mcuadros/go-version"
|
"github.com/mcuadros/go-version"
|
||||||
)
|
)
|
||||||
|
|
||||||
const TAG_PREFIX = "refs/tags/"
|
// TagPrefix tags prefix path on the repository
|
||||||
|
const TagPrefix = "refs/tags/"
|
||||||
|
|
||||||
// IsTagExist returns true if given tag exists in the repository.
|
// IsTagExist returns true if given tag exists in the repository.
|
||||||
func IsTagExist(repoPath, name string) bool {
|
func IsTagExist(repoPath, name string) bool {
|
||||||
return IsReferenceExist(repoPath, TAG_PREFIX+name)
|
return IsReferenceExist(repoPath, TagPrefix+name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsTagExist returns true if given tag exists in the repository.
|
||||||
func (repo *Repository) IsTagExist(name string) bool {
|
func (repo *Repository) IsTagExist(name string) bool {
|
||||||
return IsTagExist(repo.Path, name)
|
return IsTagExist(repo.Path, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateTag create one tag in the repository
|
||||||
func (repo *Repository) CreateTag(name, revision string) error {
|
func (repo *Repository) CreateTag(name, revision string) error {
|
||||||
_, err := NewCommand("tag", name, revision).RunInDir(repo.Path)
|
_, err := NewCommand("tag", name, revision).RunInDir(repo.Path)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (repo *Repository) getTag(id sha1) (*Tag, error) {
|
func (repo *Repository) getTag(id SHA1) (*Tag, error) {
|
||||||
t, ok := repo.tagCache.Get(id.String())
|
t, ok := repo.tagCache.Get(id.String())
|
||||||
if ok {
|
if ok {
|
||||||
log("Hit cache: %s", id)
|
log("Hit cache: %s", id)
|
||||||
|
@ -41,11 +44,11 @@ func (repo *Repository) getTag(id sha1) (*Tag, error) {
|
||||||
tp = strings.TrimSpace(tp)
|
tp = strings.TrimSpace(tp)
|
||||||
|
|
||||||
// Tag is a commit.
|
// Tag is a commit.
|
||||||
if ObjectType(tp) == OBJECT_COMMIT {
|
if ObjectType(tp) == ObjectCommit {
|
||||||
tag := &Tag{
|
tag := &Tag{
|
||||||
ID: id,
|
ID: id,
|
||||||
Object: id,
|
Object: id,
|
||||||
Type: string(OBJECT_COMMIT),
|
Type: string(ObjectCommit),
|
||||||
repo: repo,
|
repo: repo,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
package git
|
package git
|
||||||
|
|
||||||
func (repo *Repository) getTree(id sha1) (*Tree, error) {
|
func (repo *Repository) getTree(id SHA1) (*Tree, error) {
|
||||||
treePath := filepathFromSHA1(repo.Path, id.String())
|
treePath := filepathFromSHA1(repo.Path, id.String())
|
||||||
if isFile(treePath) {
|
if isFile(treePath) {
|
||||||
_, err := NewCommand("ls-tree", id.String()).RunInDir(repo.Path)
|
_, err := NewCommand("ls-tree", id.String()).RunInDir(repo.Path)
|
||||||
|
@ -16,7 +16,7 @@ func (repo *Repository) getTree(id sha1) (*Tree, error) {
|
||||||
return NewTree(repo, id), nil
|
return NewTree(repo, id), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find the tree object in the repository.
|
// GetTree find the tree object in the repository.
|
||||||
func (repo *Repository) GetTree(idStr string) (*Tree, error) {
|
func (repo *Repository) GetTree(idStr string) (*Tree, error) {
|
||||||
id, err := NewIDFromString(idStr)
|
id, err := NewIDFromString(idStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -10,13 +10,15 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
const EMPTY_SHA = "0000000000000000000000000000000000000000"
|
// EmptySHA defines empty git SHA
|
||||||
|
const EmptySHA = "0000000000000000000000000000000000000000"
|
||||||
|
|
||||||
type sha1 [20]byte
|
// SHA1 a git commit name
|
||||||
|
type SHA1 [20]byte
|
||||||
|
|
||||||
// Equal returns true if s has the same sha1 as caller.
|
// Equal returns true if s has the same SHA1 as caller.
|
||||||
// Support 40-length-string, []byte, sha1.
|
// Support 40-length-string, []byte, SHA1.
|
||||||
func (id sha1) Equal(s2 interface{}) bool {
|
func (id SHA1) Equal(s2 interface{}) bool {
|
||||||
switch v := s2.(type) {
|
switch v := s2.(type) {
|
||||||
case string:
|
case string:
|
||||||
if len(v) != 40 {
|
if len(v) != 40 {
|
||||||
|
@ -32,7 +34,7 @@ func (id sha1) Equal(s2 interface{}) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case sha1:
|
case SHA1:
|
||||||
for i, v := range v {
|
for i, v := range v {
|
||||||
if id[i] != v {
|
if id[i] != v {
|
||||||
return false
|
return false
|
||||||
|
@ -45,42 +47,42 @@ func (id sha1) Equal(s2 interface{}) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// String returns string (hex) representation of the Oid.
|
// String returns string (hex) representation of the Oid.
|
||||||
func (s sha1) String() string {
|
func (id SHA1) String() string {
|
||||||
result := make([]byte, 0, 40)
|
result := make([]byte, 0, 40)
|
||||||
hexvalues := []byte("0123456789abcdef")
|
hexvalues := []byte("0123456789abcdef")
|
||||||
for i := 0; i < 20; i++ {
|
for i := 0; i < 20; i++ {
|
||||||
result = append(result, hexvalues[s[i]>>4])
|
result = append(result, hexvalues[id[i]>>4])
|
||||||
result = append(result, hexvalues[s[i]&0xf])
|
result = append(result, hexvalues[id[i]&0xf])
|
||||||
}
|
}
|
||||||
return string(result)
|
return string(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MustID always creates a new sha1 from a [20]byte array with no validation of input.
|
// MustID always creates a new SHA1 from a [20]byte array with no validation of input.
|
||||||
func MustID(b []byte) sha1 {
|
func MustID(b []byte) SHA1 {
|
||||||
var id sha1
|
var id SHA1
|
||||||
for i := 0; i < 20; i++ {
|
for i := 0; i < 20; i++ {
|
||||||
id[i] = b[i]
|
id[i] = b[i]
|
||||||
}
|
}
|
||||||
return id
|
return id
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewID creates a new sha1 from a [20]byte array.
|
// NewID creates a new SHA1 from a [20]byte array.
|
||||||
func NewID(b []byte) (sha1, error) {
|
func NewID(b []byte) (SHA1, error) {
|
||||||
if len(b) != 20 {
|
if len(b) != 20 {
|
||||||
return sha1{}, fmt.Errorf("Length must be 20: %v", b)
|
return SHA1{}, fmt.Errorf("Length must be 20: %v", b)
|
||||||
}
|
}
|
||||||
return MustID(b), nil
|
return MustID(b), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// MustIDFromString always creates a new sha from a ID with no validation of input.
|
// MustIDFromString always creates a new sha from a ID with no validation of input.
|
||||||
func MustIDFromString(s string) sha1 {
|
func MustIDFromString(s string) SHA1 {
|
||||||
b, _ := hex.DecodeString(s)
|
b, _ := hex.DecodeString(s)
|
||||||
return MustID(b)
|
return MustID(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewIDFromString creates a new sha1 from a ID string of length 40.
|
// NewIDFromString creates a new SHA1 from a ID string of length 40.
|
||||||
func NewIDFromString(s string) (sha1, error) {
|
func NewIDFromString(s string) (SHA1, error) {
|
||||||
var id sha1
|
var id SHA1
|
||||||
s = strings.TrimSpace(s)
|
s = strings.TrimSpace(s)
|
||||||
if len(s) != 40 {
|
if len(s) != 40 {
|
||||||
return id, fmt.Errorf("Length must be 40: %s", s)
|
return id, fmt.Errorf("Length must be 40: %s", s)
|
||||||
|
|
|
@ -6,6 +6,7 @@ package git
|
||||||
|
|
||||||
import "strings"
|
import "strings"
|
||||||
|
|
||||||
|
// SubModule submodule is a reference on git repository
|
||||||
type SubModule struct {
|
type SubModule struct {
|
||||||
Name string
|
Name string
|
||||||
URL string
|
URL string
|
||||||
|
@ -19,6 +20,7 @@ type SubModuleFile struct {
|
||||||
refID string
|
refID string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewSubModuleFile create a new submodule file
|
||||||
func NewSubModuleFile(c *Commit, refURL, refID string) *SubModuleFile {
|
func NewSubModuleFile(c *Commit, refURL, refID string) *SubModuleFile {
|
||||||
return &SubModuleFile{
|
return &SubModuleFile{
|
||||||
Commit: c,
|
Commit: c,
|
||||||
|
@ -64,9 +66,8 @@ func (sf *SubModuleFile) RefURL(urlPrefix string, parentPath string) string {
|
||||||
// fix problem with reverse proxy works only with local server
|
// fix problem with reverse proxy works only with local server
|
||||||
if strings.Contains(urlPrefix, url[i+1:j]) {
|
if strings.Contains(urlPrefix, url[i+1:j]) {
|
||||||
return urlPrefix + url[j+1:]
|
return urlPrefix + url[j+1:]
|
||||||
} else {
|
|
||||||
return "http://" + url[i+1:j] + "/" + url[j+1:]
|
|
||||||
}
|
}
|
||||||
|
return "http://" + url[i+1:j] + "/" + url[j+1:]
|
||||||
}
|
}
|
||||||
|
|
||||||
return url
|
return url
|
||||||
|
|
|
@ -9,14 +9,15 @@ import "bytes"
|
||||||
// Tag represents a Git tag.
|
// Tag represents a Git tag.
|
||||||
type Tag struct {
|
type Tag struct {
|
||||||
Name string
|
Name string
|
||||||
ID sha1
|
ID SHA1
|
||||||
repo *Repository
|
repo *Repository
|
||||||
Object sha1 // The id of this commit object
|
Object SHA1 // The id of this commit object
|
||||||
Type string
|
Type string
|
||||||
Tagger *Signature
|
Tagger *Signature
|
||||||
Message string
|
Message string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Commit return the commit of the tag reference
|
||||||
func (tag *Tag) Commit() (*Commit, error) {
|
func (tag *Tag) Commit() (*Commit, error) {
|
||||||
return tag.repo.getCommit(tag.Object)
|
return tag.repo.getCommit(tag.Object)
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ import (
|
||||||
|
|
||||||
// Tree represents a flat directory listing.
|
// Tree represents a flat directory listing.
|
||||||
type Tree struct {
|
type Tree struct {
|
||||||
ID sha1
|
ID SHA1
|
||||||
repo *Repository
|
repo *Repository
|
||||||
|
|
||||||
// parent tree
|
// parent tree
|
||||||
|
@ -22,7 +22,8 @@ type Tree struct {
|
||||||
entriesParsed bool
|
entriesParsed bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTree(repo *Repository, id sha1) *Tree {
|
// NewTree create a new tree according the repository and commit id
|
||||||
|
func NewTree(repo *Repository, id SHA1) *Tree {
|
||||||
return &Tree{
|
return &Tree{
|
||||||
ID: id,
|
ID: id,
|
||||||
repo: repo,
|
repo: repo,
|
||||||
|
@ -63,22 +64,22 @@ func parseTreeData(tree *Tree, data []byte) ([]*TreeEntry, error) {
|
||||||
step := 6
|
step := 6
|
||||||
switch string(data[pos : pos+step]) {
|
switch string(data[pos : pos+step]) {
|
||||||
case "100644":
|
case "100644":
|
||||||
entry.mode = ENTRY_MODE_BLOB
|
entry.mode = EntryModeBlob
|
||||||
entry.Type = OBJECT_BLOB
|
entry.Type = ObjectBlob
|
||||||
case "100755":
|
case "100755":
|
||||||
entry.mode = ENTRY_MODE_EXEC
|
entry.mode = EntryModeExec
|
||||||
entry.Type = OBJECT_BLOB
|
entry.Type = ObjectBlob
|
||||||
case "120000":
|
case "120000":
|
||||||
entry.mode = ENTRY_MODE_SYMLINK
|
entry.mode = EntryModeSymlink
|
||||||
entry.Type = OBJECT_BLOB
|
entry.Type = ObjectBlob
|
||||||
case "160000":
|
case "160000":
|
||||||
entry.mode = ENTRY_MODE_COMMIT
|
entry.mode = EntryModeCommit
|
||||||
entry.Type = OBJECT_COMMIT
|
entry.Type = ObjectCommit
|
||||||
|
|
||||||
step = 8
|
step = 8
|
||||||
case "040000":
|
case "040000":
|
||||||
entry.mode = ENTRY_MODE_TREE
|
entry.mode = EntryModeTree
|
||||||
entry.Type = OBJECT_TREE
|
entry.Type = ObjectTree
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unknown type: %v", string(data[pos:pos+step]))
|
return nil, fmt.Errorf("unknown type: %v", string(data[pos:pos+step]))
|
||||||
}
|
}
|
||||||
|
@ -90,7 +91,7 @@ func parseTreeData(tree *Tree, data []byte) ([]*TreeEntry, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
entry.ID = id
|
entry.ID = id
|
||||||
pos += step + 1 // Skip half of sha1.
|
pos += step + 1 // Skip half of SHA1.
|
||||||
|
|
||||||
step = bytes.IndexByte(data[pos:], '\n')
|
step = bytes.IndexByte(data[pos:], '\n')
|
||||||
|
|
||||||
|
@ -107,6 +108,7 @@ func parseTreeData(tree *Tree, data []byte) ([]*TreeEntry, error) {
|
||||||
return entries, nil
|
return entries, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SubTree get a sub tree by the sub dir path
|
||||||
func (t *Tree) SubTree(rpath string) (*Tree, error) {
|
func (t *Tree) SubTree(rpath string) (*Tree, error) {
|
||||||
if len(rpath) == 0 {
|
if len(rpath) == 0 {
|
||||||
return t, nil
|
return t, nil
|
||||||
|
|
|
@ -9,12 +9,13 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// GetTreeEntryByPath get the tree entries accroding the sub dir
|
||||||
func (t *Tree) GetTreeEntryByPath(relpath string) (*TreeEntry, error) {
|
func (t *Tree) GetTreeEntryByPath(relpath string) (*TreeEntry, error) {
|
||||||
if len(relpath) == 0 {
|
if len(relpath) == 0 {
|
||||||
return &TreeEntry{
|
return &TreeEntry{
|
||||||
ID: t.ID,
|
ID: t.ID,
|
||||||
Type: OBJECT_TREE,
|
Type: ObjectTree,
|
||||||
mode: ENTRY_MODE_TREE,
|
mode: EntryModeTree,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,6 +44,7 @@ func (t *Tree) GetTreeEntryByPath(relpath string) (*TreeEntry, error) {
|
||||||
return nil, ErrNotExist{"", relpath}
|
return nil, ErrNotExist{"", relpath}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetBlobByPath get the blob object accroding the path
|
||||||
func (t *Tree) GetBlobByPath(relpath string) (*Blob, error) {
|
func (t *Tree) GetBlobByPath(relpath string) (*Blob, error) {
|
||||||
entry, err := t.GetTreeEntryByPath(relpath)
|
entry, err := t.GetTreeEntryByPath(relpath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -14,20 +14,27 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// EntryMode the type of the object in the git tree
|
||||||
type EntryMode int
|
type EntryMode int
|
||||||
|
|
||||||
// There are only a few file modes in Git. They look like unix file modes, but they can only be
|
// There are only a few file modes in Git. They look like unix file modes, but they can only be
|
||||||
// one of these.
|
// one of these.
|
||||||
const (
|
const (
|
||||||
ENTRY_MODE_BLOB EntryMode = 0100644
|
// EntryModeBlob
|
||||||
ENTRY_MODE_EXEC EntryMode = 0100755
|
EntryModeBlob EntryMode = 0100644
|
||||||
ENTRY_MODE_SYMLINK EntryMode = 0120000
|
// EntryModeExec
|
||||||
ENTRY_MODE_COMMIT EntryMode = 0160000
|
EntryModeExec EntryMode = 0100755
|
||||||
ENTRY_MODE_TREE EntryMode = 0040000
|
// EntryModeSymlink
|
||||||
|
EntryModeSymlink EntryMode = 0120000
|
||||||
|
// EntryModeCommit
|
||||||
|
EntryModeCommit EntryMode = 0160000
|
||||||
|
// EntryModeTree
|
||||||
|
EntryModeTree EntryMode = 0040000
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TreeEntry the leaf in the git tree
|
||||||
type TreeEntry struct {
|
type TreeEntry struct {
|
||||||
ID sha1
|
ID SHA1
|
||||||
Type ObjectType
|
Type ObjectType
|
||||||
|
|
||||||
mode EntryMode
|
mode EntryMode
|
||||||
|
@ -41,10 +48,12 @@ type TreeEntry struct {
|
||||||
sized bool
|
sized bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Name returns the name of the entry
|
||||||
func (te *TreeEntry) Name() string {
|
func (te *TreeEntry) Name() string {
|
||||||
return te.name
|
return te.name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Size returns the size of the entry
|
||||||
func (te *TreeEntry) Size() int64 {
|
func (te *TreeEntry) Size() int64 {
|
||||||
if te.IsDir() {
|
if te.IsDir() {
|
||||||
return 0
|
return 0
|
||||||
|
@ -62,14 +71,22 @@ func (te *TreeEntry) Size() int64 {
|
||||||
return te.size
|
return te.size
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsSubModule if the entry is a sub module
|
||||||
func (te *TreeEntry) IsSubModule() bool {
|
func (te *TreeEntry) IsSubModule() bool {
|
||||||
return te.mode == ENTRY_MODE_COMMIT
|
return te.mode == EntryModeCommit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsDir if the entry is a sub dir
|
||||||
func (te *TreeEntry) IsDir() bool {
|
func (te *TreeEntry) IsDir() bool {
|
||||||
return te.mode == ENTRY_MODE_TREE
|
return te.mode == EntryModeTree
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsLink if the entry is a symlink
|
||||||
|
func (te *TreeEntry) IsLink() bool {
|
||||||
|
return te.mode == EntryModeSymlink
|
||||||
|
}
|
||||||
|
|
||||||
|
// Blob retrun the blob object the entry
|
||||||
func (te *TreeEntry) Blob() *Blob {
|
func (te *TreeEntry) Blob() *Blob {
|
||||||
return &Blob{
|
return &Blob{
|
||||||
repo: te.ptree.repo,
|
repo: te.ptree.repo,
|
||||||
|
@ -77,6 +94,7 @@ func (te *TreeEntry) Blob() *Blob {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Entries a list of entry
|
||||||
type Entries []*TreeEntry
|
type Entries []*TreeEntry
|
||||||
|
|
||||||
var sorter = []func(t1, t2 *TreeEntry) bool{
|
var sorter = []func(t1, t2 *TreeEntry) bool{
|
||||||
|
@ -105,6 +123,7 @@ func (tes Entries) Less(i, j int) bool {
|
||||||
return sorter[k](t1, t2)
|
return sorter[k](t1, t2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sort sort the list of entry
|
||||||
func (tes Entries) Sort() {
|
func (tes Entries) Sort() {
|
||||||
sort.Sort(tes)
|
sort.Sort(tes)
|
||||||
}
|
}
|
||||||
|
@ -167,7 +186,7 @@ func (tes Entries) GetCommitsInfoWithCustomConcurrency(commit *Commit, treePath
|
||||||
// However when taskChan is full, code will block and wait any running goroutines to finish.
|
// However when taskChan is full, code will block and wait any running goroutines to finish.
|
||||||
taskChan <- true
|
taskChan <- true
|
||||||
|
|
||||||
if tes[i].Type != OBJECT_COMMIT {
|
if tes[i].Type != ObjectCommit {
|
||||||
go func(i int) {
|
go func(i int) {
|
||||||
cinfo := commitInfo{entryName: tes[i].Name()}
|
cinfo := commitInfo{entryName: tes[i].Name()}
|
||||||
c, err := commit.GetCommitByPath(filepath.Join(treePath, tes[i].Name()))
|
c, err := commit.GetCommitByPath(filepath.Join(treePath, tes[i].Name()))
|
||||||
|
|
|
@ -12,26 +12,28 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
// objectCache provides thread-safe cache opeations.
|
// ObjectCache provides thread-safe cache opeations.
|
||||||
type objectCache struct {
|
type ObjectCache struct {
|
||||||
lock sync.RWMutex
|
lock sync.RWMutex
|
||||||
cache map[string]interface{}
|
cache map[string]interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newObjectCache() *objectCache {
|
func newObjectCache() *ObjectCache {
|
||||||
return &objectCache{
|
return &ObjectCache{
|
||||||
cache: make(map[string]interface{}, 10),
|
cache: make(map[string]interface{}, 10),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (oc *objectCache) Set(id string, obj interface{}) {
|
// Set add obj to cache
|
||||||
|
func (oc *ObjectCache) Set(id string, obj interface{}) {
|
||||||
oc.lock.Lock()
|
oc.lock.Lock()
|
||||||
defer oc.lock.Unlock()
|
defer oc.lock.Unlock()
|
||||||
|
|
||||||
oc.cache[id] = obj
|
oc.cache[id] = obj
|
||||||
}
|
}
|
||||||
|
|
||||||
func (oc *objectCache) Get(id string) (interface{}, bool) {
|
// Get get cached obj by id
|
||||||
|
func (oc *ObjectCache) Get(id string) (interface{}, bool) {
|
||||||
oc.lock.RLock()
|
oc.lock.RLock()
|
||||||
defer oc.lock.RUnlock()
|
defer oc.lock.RUnlock()
|
||||||
|
|
||||||
|
@ -80,13 +82,14 @@ func filepathFromSHA1(rootdir, sha1 string) string {
|
||||||
return filepath.Join(rootdir, "objects", sha1[:2], sha1[2:])
|
return filepath.Join(rootdir, "objects", sha1[:2], sha1[2:])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RefEndName return the end name of a ref name
|
||||||
func RefEndName(refStr string) string {
|
func RefEndName(refStr string) string {
|
||||||
if strings.HasPrefix(refStr, BRANCH_PREFIX) {
|
if strings.HasPrefix(refStr, BranchPrefix) {
|
||||||
return refStr[len(BRANCH_PREFIX):]
|
return refStr[len(BranchPrefix):]
|
||||||
}
|
}
|
||||||
|
|
||||||
if strings.HasPrefix(refStr, TAG_PREFIX) {
|
if strings.HasPrefix(refStr, TagPrefix) {
|
||||||
return refStr[len(TAG_PREFIX):]
|
return refStr[len(TagPrefix):]
|
||||||
}
|
}
|
||||||
|
|
||||||
return refStr
|
return refStr
|
||||||
|
|
|
@ -3,10 +3,10 @@
|
||||||
"ignore": "test",
|
"ignore": "test",
|
||||||
"package": [
|
"package": [
|
||||||
{
|
{
|
||||||
"checksumSHA1": "X4WaxEtgFkM4VHg6TcNk2xkrqCI=",
|
"checksumSHA1": "OWuUWQ8sWC8n+eTQttx+3vfES8g=",
|
||||||
"path": "code.gitea.io/git",
|
"path": "code.gitea.io/git",
|
||||||
"revision": "0807b517283977be34f0ff5510b21e676fc1527c",
|
"revision": "634abd6a61c350a95f6b146c3a5fc323282608ae",
|
||||||
"revisionTime": "2016-11-13T14:20:52Z"
|
"revisionTime": "2016-12-22T08:49:21Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "KZEYDYPzVc12f0770V++kIHhfa0=",
|
"checksumSHA1": "KZEYDYPzVc12f0770V++kIHhfa0=",
|
||||||
|
|
Loading…
Reference in New Issue