-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Create new branch from branch selection dropdown #2130
Create new branch from branch selection dropdown #2130
Conversation
12ac6e2
to
25d4619
Compare
486a881
to
8646ee5
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
and a question
models/repo.go
Outdated
return fmt.Errorf("UpdateLocalCopyBranch: %v", err) | ||
} | ||
|
||
if err = repo.CheckoutNewBranch(commit, branchName); err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you sure that we need a clone to do this? Since one can create new branches in a bare repo...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also see https://github.com/go-gitea/gitea/blob/master/models/repo.go#L2341 for reference
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is for creating new branch based on existing branch/tag/commit, so no bare repositories. I was basing it exactly on your pointed out code. Branch can be created directly in real repository without cloning but I just thought that there are reasons why existing branch creation from branch was made this way
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I noticed that after I commented on it. I wonder why it does that 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is very good question that I would really be interested to get answer, it could be just for that it is also used by file editor but could be also other reasons, hard to tell
routers/routes/routes.go
Outdated
@@ -523,6 +523,10 @@ func RegisterRoutes(m *macaron.Macaron) { | |||
return | |||
} | |||
}) | |||
|
|||
m.Group("/branches", func() { | |||
m.Get("/_new/*", context.RepoRef(), repo.CreateBranch) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why _new
instead of new
? (Except that we'd need to add another reserved name)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just to clarify, I'm fairly certain that no one would create a branch called "new" anyhow 😆
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was following same naming rules that are for editing files and I don't think we need to add new reserved name, it is completely new group under repo
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this be POST
? GET
requests shouldn't update state.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ethantkoenig it would be right way but semantic-ui dropdown does not have enough events exposed to do that :(
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@lafriks Can't we just add some Javascript/JQuery to make a POST request?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ethantkoenig not with jquery if only doing with some js timeouts to check if semantic ui dropdown has rendered that link and than binding on click but that is quite dirty that I would want to avoid. Also it could be done using <a onclick
with bunch of javascript but that code also seems more like hack than good solution
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@lafriks If we use GET
, then there will no be CSRF check
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@lafriks What do you think of this: https://github.com/lafriks/gitea/pull/1?
models/repo.go
Outdated
@@ -2363,3 +2401,28 @@ func (repo *Repository) CreateNewBranch(doer *User, oldBranchName, branchName st | |||
|
|||
return nil | |||
} | |||
|
|||
// CreateNewBranchFromCommit creates a new repository branch | |||
func (repo *Repository) CreateNewBranchFromCommit(doer *User, commit, branchName string) (err error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please put this function in models/repo_branch.go
instead. Let's not add more to the God File unless we have to 😄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should I also move CreateNewBranch
function that was already there to repo_branch.go? As these functions are doing very similar things I just did not want them to be in different files
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@lafriks Yes please 🙂
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe if possible make it the same function (renaming oldBranch
to baseRef
or similar)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They have different functionality a bit and different checks and some code is called in different order, so making all one function would overcomplicate it
integrations/repo_branch_test.go
Outdated
func testCreateBranch(t *testing.T, session *TestSession, user, repo, oldRefName, newBranchName string, expectedStatus int) string { | ||
req := NewRequest(t, "GET", path.Join(user, repo, "branches/_new", oldRefName)+"?name="+url.QueryEscape(newBranchName)) | ||
resp := session.MakeRequest(t, req, expectedStatus) | ||
assert.EqualValues(t, expectedStatus, resp.HeaderCode) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This line isn't necessary, session.MakeRequest(..)
already checks
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will fix, I did write this code before MakeRequest was changed and forgot to remove that line
routers/routes/routes.go
Outdated
@@ -523,6 +523,10 @@ func RegisterRoutes(m *macaron.Macaron) { | |||
return | |||
} | |||
}) | |||
|
|||
m.Group("/branches", func() { | |||
m.Get("/_new/*", context.RepoRef(), repo.CreateBranch) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this be POST
? GET
requests shouldn't update state.
templates/repo/branch_dropdown.tmpl
Outdated
@@ -1,6 +1,6 @@ | |||
<div class="fitted item choose reference"> | |||
<div class="ui floating filter dropdown" data-no-results="{{.i18n.Tr "repo.pulls.no_results"}}"> | |||
<div class="ui basic small button"> | |||
<div class="ui floating filter dropdown" data-can-create-branch="{{if .Repository.CanCreateBranch}}true{{else}}false{{end}}" data-no-results="{{.i18n.Tr "repo.pulls.no_results"}}"> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can just do data-can-create-branch="{{.Repository.CanCreateBranch}}"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did not work for me...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
f29ceae
to
29997f0
Compare
@bkcsoft, @ethantkoenig fixed with all your suggestions, please review |
a8d9b11
to
835cc95
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
1 question, 1 suggestion. Otherwise LGTM
routers/repo/branch.go
Outdated
return | ||
} | ||
|
||
if len(form.NewBranchName) > 100 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where is this limit taken from? Why is it not a constant? (in go-gitea/git
) :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bkcsoft it is limit used also in other places in gitea code where branch name can be entered (editor for example)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No clue 😆 Hence why I'm asking 😉
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bkcsoft @ethantkoenig suggested that limit and later I found it also in other places in code, so that's why it is so :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suggested it because I found it in other places 😆
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And I'm suggesting making that a global constant variable in code.gitea.io/git
routers/repo/branch.go
Outdated
} | ||
|
||
if _, err := ctx.Repo.GitRepo.GetTag(form.NewBranchName); err == nil { | ||
ctx.Flash.Error(ctx.Tr("repo.branch.tag_already_exists", form.NewBranchName)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should probably be repo.branch.tag_collision
835cc95
to
3e972ad
Compare
As things currently are, I can't create a branch called "mast", since searing for "mast" will match the master branch (so the "Create new branch" button won't show). Maybe the button should always show, but be disabled for exact matches? |
You are right but with this dropdown I don't know if it is possible. I will check if I can work around somehow |
e82b933
to
f2d291a
Compare
@ethantkoenig @bkcsoft I rewrote javascript side to allow creating branches also with names that partially match existing branches as suggested, please review |
1770bae
to
c21cf76
Compare
As I could not solve small remaining issue that no data found would still show up sometimes together with create new branch item, I did complete JavaScript branch/tag dropdown rewrite to not use semantic ui js but VueJS. As a plus works a lot faster and in future more extendable. |
650e35b
to
fd7e742
Compare
Codecov Report
@@ Coverage Diff @@
## master #2130 +/- ##
==========================================
- Coverage 27.1% 26.98% -0.12%
==========================================
Files 87 87
Lines 17191 17283 +92
==========================================
+ Hits 4659 4664 +5
- Misses 11852 11939 +87
Partials 680 680
Continue to review full report at Codecov.
|
213b08e
to
7423bbd
Compare
@ethantkoenig need your approval |
7423bbd
to
eae7ad5
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
models/repo_branch.go
Outdated
// This function checks out target commit by default, it is safe to assume subsequent | ||
// operations are operating against target commit when caller has confidence for no race condition. | ||
func UpdateLocalCopyToCommit(repoPath, localPath, commit string) error { | ||
if !com.IsExist(localPath) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe this is duplicated code; this if
/else
can be replaced by
UpdateLocalCopyBranch(repoPath, localPath, repo.DefaultBranch)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ethantkoenig no it can not as they are different and calls methods in different order. Code from UpdateLocalCopyBranch will fail in some cases if not updated in correct order (at least for creating new branch from commit/tag)
models/repo_branch.go
Outdated
// It creates a new clone if local copy does not exist. | ||
// This function checks out target commit by default, it is safe to assume subsequent | ||
// operations are operating against target commit when caller has confidence for no race condition. | ||
func UpdateLocalCopyToCommit(repoPath, localPath, commit string) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since the function should only be called when the repo's "mutex" has been acquired, should we make it private? Similarly, for repo.UpdateLocalCopyToCommit
?
routers/repo/branch.go
Outdated
ctx.Flash.Error(ctx.Tr("repo.branch.branch_already_exists", branch.Name)) | ||
ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchName) | ||
return | ||
} else if (len(branch.Name) < len(form.NewBranchName) && branch.Name+"/" == form.NewBranchName[0:len(branch.Name)+1]) || |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we move this condition to a helper function, something like BranchNamesConflict(name1, name2)
? It could be useful in other places, and would make the code more readable IMO.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ethantkoenig I have no idea to where to move this functions
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
models.(*Repository).BranchNameConflict(form.NewBranchName)
perhaps?
@ethantkoenig from what it looks like from screenshot you are not logged in. For branch creation to show up you must be logged in and have write access to that repository |
@lafriks Wow, not sure how I missed that 🤦♂️. Links appearing as they should. |
index.css gets created by make stylesheets from less file. So shouldn't matter. can someone finish the job, I'm waiting for this feature :) |
dbb75f0
to
f7ebb60
Compare
LGTM |
f7ebb60
to
bdd64d4
Compare
errs.Add([]string{name}, ErrGitRefName, "GitRefName") | ||
return false, errs | ||
} | ||
parts := strings.Split(str, "/") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about using a regex instead of doing similar checks twice? https://stackoverflow.com/a/12093994
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That won't work in golang as its regex does not support all required features. I tried that exact stackoverflow topic regex first when implementing this
routers/repo/branch.go
Outdated
ctx.Flash.Error(ctx.Tr("repo.branch.branch_already_exists", branch.Name)) | ||
ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchName) | ||
return | ||
} else if (len(branch.Name) < len(form.NewBranchName) && branch.Name+"/" == form.NewBranchName[0:len(branch.Name)+1]) || |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
models.(*Repository).BranchNameConflict(form.NewBranchName)
perhaps?
@ethantkoenig @bkcsoft moved branch name validation to model |
models/repo_branch.go
Outdated
if err := repo.CheckBranchName(branchName); err != nil { | ||
return err | ||
} | ||
|
||
repoWorkingPool.CheckIn(com.ToStr(repo.ID)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do this before checking the branch-name, otherwise you'll end up with race conditions
models/repo_branch.go
Outdated
if err := repo.CheckBranchName(branchName); err != nil { | ||
return err | ||
} | ||
|
||
repoWorkingPool.CheckIn(com.ToStr(repo.ID)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bkcsoft fixed both
routers/repo/branch.go
Outdated
} else { | ||
err = ctx.Repo.Repository.CreateNewBranchFromCommit(ctx.User, ctx.Repo.BranchName, form.NewBranchName) | ||
} | ||
if err != nil { | ||
ctx.Handle(500, "CreateNewBranch", err) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so 500 error if the name is invalid?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No there is L56-L73 special error type checks before this - this one is for unexpected errors
return err | ||
} | ||
|
||
for _, branch := range branches { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of doing this, how about something like this that scales to thousands of branches?
if _, err := gitRepo.GetBranch(name); err != nil {
return ErrConflict(name)
}
if strings.Contains(name, "/") {
for index := strings.LastIndex(name, "/"); index > 0; index := strings.LastIndex(name, "/") {
if conflict, err := gitRepo.GetBranch(name[:index]); err != nil {
return ErrConflict(name[:index])
}
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is not the same (if name
is feat
and there already exists branch feat/test
) this code won't return error
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One option is to just try the git branch ...
command, and see if it succeeds or not. I assume that internally git has an efficient way to check for naming conflicts.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that gitRepo.CreateBranch
will throw a collision error no?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But then CheckBranchName
will only provide part of validation (as I understand that that was an initial request to create it at first place to reuse it in other places later). Also currently GetBranches is called on every repo page load (in ctx RepoAssignment
) so it's usage here anyway won't be that big deal
573a7d7
to
3d0a597
Compare
Also fix Branches and Tags tab alignment in branch selection select box
Screenshot: