mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-31 21:28:11 +09:00 
			
		
		
		
	Web editor: improve edit file and diff preview
This commit is contained in:
		| @@ -440,36 +440,34 @@ edit_this_file = Edit this file | |||||||
| edit_file = Edit file | edit_file = Edit file | ||||||
| delete_confirm_message = Are you sure you want to delete this file? | delete_confirm_message = Are you sure you want to delete this file? | ||||||
| delete_commit_message = Write a note about this delete (optional) | delete_commit_message = Write a note about this delete (optional) | ||||||
| file_editing_no_longer_exists = The file you are editing no longer exists in the repository |  | ||||||
| file_already_exists = A file by that name already exists |  | ||||||
| unable_to_update_file = Unable to update this file, error occurred | unable_to_update_file = Unable to update this file, error occurred | ||||||
| add = Add |  | ||||||
| update = Update |  | ||||||
| filename_cannot_be_empty = Filename cannot be empty |  | ||||||
| directory_is_a_file = One of the directories in the path is already a file in this repository |  | ||||||
| filename_is_a_directory = The filename given is an existing directory in the repository |  | ||||||
| must_be_on_branch = You must be on a branch to make or propose changes to this file | must_be_on_branch = You must be on a branch to make or propose changes to this file | ||||||
| must_be_writer = You must have write access to make or propose changes to this file | must_be_writer = You must have write access to make or propose changes to this file | ||||||
| cannot_edit_binary_files = Cannot edit binary files | cannot_edit_binary_files = Cannot edit binary files | ||||||
| filename_help = To add directory, just type it and press /. To remove a directory, go to the beginning of the field and press backspace. | filename_help = To add directory, just type it and press /. To remove a directory, go to the beginning of the field and press backspace. | ||||||
| fork_before_edit = You must fork this before editing | fork_before_edit = You must fork this before editing | ||||||
| branch_already_exists = Branch already exists |  | ||||||
| create_new_branch = Create a %s for this commit and start a pull request. |  | ||||||
| new_branch = new branch | new_branch = new branch | ||||||
| commit_directly_to_this_branch = Commit directly to the %s branch. | editor.commit_directly_to_this_branch = Commit directly to the <strong class="branch-name">%s</strong> branch. | ||||||
|  | editor.create_new_branch = Create a <strong>new branch</strong> for this commit and start a pull request. | ||||||
|  | editor.filename_cannot_be_empty = Filename cannot be empty. | ||||||
|  | editor.branch_already_exists = Branch '%s' already exists in this repository. | ||||||
|  | editor.directory_is_a_file = Entry '%s' in the parent path is a file not a directory in this repository. | ||||||
|  | editor.filename_is_a_directory = The filename '%s' is an existing directory in this repository. | ||||||
|  | editor.file_editing_no_longer_exists = The file '%s' you are editing no longer exists in the repository. | ||||||
|  | editor.file_changed_while_editing = File content has been changed since you started editing. <a target="_blank" href="%s">Click here</a> to see what have been changed or <strong>press commit again</strong> to overwrite those changes. | ||||||
|  | editor.file_already_exists = A file with name '%s' already exists in this repository. | ||||||
|  | editor.add = Add '%s' | ||||||
|  | editor.update = Update '%s' | ||||||
|  | editor.failed_to_upload_files = An error occurred while updating file: %v | ||||||
|  | editor.no_changes_to_show = There are no changes to show. | ||||||
| create_branch = Create branch | create_branch = Create branch | ||||||
| from = from | from = from | ||||||
| upload_file = Upload file | upload_file = Upload file | ||||||
| add_files_to_dir = Add files to %s | add_files_to_dir = Add files to %s | ||||||
| unable_to_upload_files = Unable to upload files, an error occurred. |  | ||||||
| add_subdir = Add subdirectory... | add_subdir = Add subdirectory... | ||||||
| name_your_file = Name your file... | name_your_file = Name your file... | ||||||
| user_has_committed_since_you_started_editing = %s has committed since you started editing. |  | ||||||
| see_what_changed = See what changed. |  | ||||||
| pressing_commit_again_will_overwrite_those_changes = Pressing '%s' again will overwrite those changes. |  | ||||||
| copy_file_path_to_clipboard = Copy file path to clipboard | copy_file_path_to_clipboard = Copy file path to clipboard | ||||||
| preview_changes = Preview Changes | preview_changes = Preview Changes | ||||||
| no_changes_to_show = There are no changes to show. |  | ||||||
|  |  | ||||||
| commits.commits = Commits | commits.commits = Commits | ||||||
| commits.search = Search commits | commits.search = Search commits | ||||||
|   | |||||||
| @@ -434,7 +434,7 @@ func GetDiffRange(repoPath, beforeCommitID, afterCommitID string, maxLines, maxL | |||||||
| 		return nil, fmt.Errorf("Start: %v", err) | 		return nil, fmt.Errorf("Start: %v", err) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	pid := process.Add(fmt.Sprintf("GetDiffRange (%s)", repoPath), cmd) | 	pid := process.Add(fmt.Sprintf("GetDiffRange [repo_path: %s]", repoPath), cmd) | ||||||
| 	defer process.Remove(pid) | 	defer process.Remove(pid) | ||||||
|  |  | ||||||
| 	diff, err := ParsePatch(maxLines, maxLineCharacteres, maxFiles, stdout) | 	diff, err := ParsePatch(maxLines, maxLineCharacteres, maxFiles, stdout) | ||||||
|   | |||||||
| @@ -334,6 +334,9 @@ var patchConflicts = []string{ | |||||||
| // testPatch checks if patch can be merged to base repository without conflit. | // testPatch checks if patch can be merged to base repository without conflit. | ||||||
| // FIXME: make a mechanism to clean up stable local copies. | // FIXME: make a mechanism to clean up stable local copies. | ||||||
| func (pr *PullRequest) testPatch() (err error) { | func (pr *PullRequest) testPatch() (err error) { | ||||||
|  | 	repoWorkingPool.CheckIn(com.ToStr(pr.BaseRepoID)) | ||||||
|  | 	defer repoWorkingPool.CheckOut(com.ToStr(pr.BaseRepoID)) | ||||||
|  |  | ||||||
| 	if pr.BaseRepo == nil { | 	if pr.BaseRepo == nil { | ||||||
| 		pr.BaseRepo, err = GetRepositoryByID(pr.BaseRepoID) | 		pr.BaseRepo, err = GetRepositoryByID(pr.BaseRepoID) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| @@ -354,20 +357,12 @@ func (pr *PullRequest) testPatch() (err error) { | |||||||
|  |  | ||||||
| 	log.Trace("PullRequest[%d].testPatch (patchPath): %s", pr.ID, patchPath) | 	log.Trace("PullRequest[%d].testPatch (patchPath): %s", pr.ID, patchPath) | ||||||
|  |  | ||||||
| 	if err := pr.BaseRepo.UpdateLocalCopy(pr.BaseRepo.DefaultBranch); err != nil { | 	if err := pr.BaseRepo.UpdateLocalCopyBranch(pr.BaseBranch); err != nil { | ||||||
| 		return fmt.Errorf("UpdateLocalCopy: %v", err) | 		return fmt.Errorf("UpdateLocalCopy: %v", err) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// Checkout base branch. |  | ||||||
| 	_, stderr, err := process.ExecDir(-1, pr.BaseRepo.LocalCopyPath(), |  | ||||||
| 		fmt.Sprintf("PullRequest.Merge (git checkout): %v", pr.BaseRepo.ID), |  | ||||||
| 		"git", "checkout", pr.BaseBranch) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return fmt.Errorf("git checkout: %s", stderr) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	pr.Status = PULL_REQUEST_STATUS_CHECKING | 	pr.Status = PULL_REQUEST_STATUS_CHECKING | ||||||
| 	_, stderr, err = process.ExecDir(-1, pr.BaseRepo.LocalCopyPath(), | 	_, stderr, err := process.ExecDir(-1, pr.BaseRepo.LocalCopyPath(), | ||||||
| 		fmt.Sprintf("testPatch (git apply --check): %d", pr.BaseRepo.ID), | 		fmt.Sprintf("testPatch (git apply --check): %d", pr.BaseRepo.ID), | ||||||
| 		"git", "apply", "--check", patchPath) | 		"git", "apply", "--check", patchPath) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
|   | |||||||
							
								
								
									
										204
									
								
								models/repo.go
									
									
									
									
									
								
							
							
						
						
									
										204
									
								
								models/repo.go
									
									
									
									
									
								
							| @@ -437,10 +437,14 @@ func (repo *Repository) DescriptionHtml() template.HTML { | |||||||
| } | } | ||||||
|  |  | ||||||
| func (repo *Repository) LocalCopyPath() string { | func (repo *Repository) LocalCopyPath() string { | ||||||
| 	return path.Join(setting.AppDataPath, "tmp/local", com.ToStr(repo.ID)) | 	return path.Join(setting.AppDataPath, "tmp/local-rpeo", com.ToStr(repo.ID)) | ||||||
| } | } | ||||||
|  |  | ||||||
| func updateLocalCopy(repoPath, localPath, branch string) error { | // UpdateLocalCopy pulls latest changes of given branch from repoPath to localPath. | ||||||
|  | // It creates a new clone if local copy does not exist. | ||||||
|  | // This function checks out target branch by default, it is safe to assume subsequent | ||||||
|  | // operations are operating against target branch when caller has confidence for no race condition. | ||||||
|  | func UpdateLocalCopyBranch(repoPath, localPath, branch string) error { | ||||||
| 	if !com.IsExist(localPath) { | 	if !com.IsExist(localPath) { | ||||||
| 		if err := git.Clone(repoPath, localPath, git.CloneRepoOptions{ | 		if err := git.Clone(repoPath, localPath, git.CloneRepoOptions{ | ||||||
| 			Timeout: time.Duration(setting.Git.Timeout.Clone) * time.Second, | 			Timeout: time.Duration(setting.Git.Timeout.Clone) * time.Second, | ||||||
| @@ -450,13 +454,11 @@ func updateLocalCopy(repoPath, localPath, branch string) error { | |||||||
| 		} | 		} | ||||||
| 	} else { | 	} else { | ||||||
| 		if err := git.Checkout(localPath, git.CheckoutOptions{ | 		if err := git.Checkout(localPath, git.CheckoutOptions{ | ||||||
| 			Branch:  branch, | 			Branch: branch, | ||||||
| 			Timeout: time.Duration(setting.Git.Timeout.Pull) * time.Second, |  | ||||||
| 		}); err != nil { | 		}); err != nil { | ||||||
| 			return fmt.Errorf("Checkout: %v", err) | 			return fmt.Errorf("Checkout: %v", err) | ||||||
| 		} | 		} | ||||||
| 		if err := git.Pull(localPath, git.PullRemoteOptions{ | 		if err := git.Pull(localPath, git.PullRemoteOptions{ | ||||||
| 			All:     false, |  | ||||||
| 			Remote:  "origin", | 			Remote:  "origin", | ||||||
| 			Branch:  branch, | 			Branch:  branch, | ||||||
| 			Timeout: time.Duration(setting.Git.Timeout.Pull) * time.Second, | 			Timeout: time.Duration(setting.Git.Timeout.Pull) * time.Second, | ||||||
| @@ -467,9 +469,9 @@ func updateLocalCopy(repoPath, localPath, branch string) error { | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| // UpdateLocalCopy makes sure the local copy of repository is up-to-date. | // UpdateLocalCopy makes sure the branch of local copy of repository is up-to-date. | ||||||
| func (repo *Repository) UpdateLocalCopy(branch string) error { | func (repo *Repository) UpdateLocalCopyBranch(branch string) error { | ||||||
| 	return updateLocalCopy(repo.RepoPath(), repo.LocalCopyPath(), branch) | 	return UpdateLocalCopyBranch(repo.RepoPath(), repo.LocalCopyPath(), branch) | ||||||
| } | } | ||||||
|  |  | ||||||
| // PatchPath returns corresponding patch file path of repository by given issue ID. | // PatchPath returns corresponding patch file path of repository by given issue ID. | ||||||
| @@ -2238,168 +2240,6 @@ func (repo *Repository) GetForks() ([]*Repository, error) { | |||||||
| 	return forks, x.Find(&forks, &Repository{ForkID: repo.ID}) | 	return forks, x.Find(&forks, &Repository{ForkID: repo.ID}) | ||||||
| } | } | ||||||
|  |  | ||||||
| // ___________    .___.__  __    ___________.__.__ |  | ||||||
| // \_   _____/  __| _/|__|/  |_  \_   _____/|__|  |   ____ |  | ||||||
| //  |    __)_  / __ | |  \   __\  |    __)  |  |  | _/ __ \ |  | ||||||
| //  |        \/ /_/ | |  ||  |    |     \   |  |  |_\  ___/ |  | ||||||
| // /_______  /\____ | |__||__|    \___  /   |__|____/\___  > |  | ||||||
| //         \/      \/                 \/                 \/ |  | ||||||
|  |  | ||||||
| func (repo *Repository) LocalRepoPath() string { |  | ||||||
| 	return path.Join(setting.AppDataPath, "tmp/local-repo", com.ToStr(repo.ID)) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // UpdateLocalRepo makes sure the local copy of repository is up-to-date. |  | ||||||
| func (repo *Repository) UpdateLocalRepo(branchName string) error { |  | ||||||
| 	return updateLocalCopy(repo.RepoPath(), repo.LocalRepoPath(), branchName) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // DiscardLocalRepoChanges makes sure the local copy of repository is the same as the source |  | ||||||
| func (repo *Repository) DiscardLocalRepoChanges(branchName string) error { |  | ||||||
| 	return discardLocalRepoChanges(repo.LocalRepoPath(), branchName) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // discardLocalRepoChanges discards local commits make sure |  | ||||||
| // it is even to remote branch when local copy exists. |  | ||||||
| func discardLocalRepoChanges(localPath string, branch string) error { |  | ||||||
| 	if !com.IsExist(localPath) { |  | ||||||
| 		return nil |  | ||||||
| 	} |  | ||||||
| 	// No need to check if nothing in the repository. |  | ||||||
| 	if !git.IsBranchExist(localPath, branch) { |  | ||||||
| 		return nil |  | ||||||
| 	} |  | ||||||
| 	if err := git.ResetHEAD(localPath, true, "origin/"+branch); err != nil { |  | ||||||
| 		return fmt.Errorf("ResetHEAD: %v", err) |  | ||||||
| 	} |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // CheckoutNewBranch checks out a new branch from the given branch name |  | ||||||
| func (repo *Repository) CheckoutNewBranch(oldBranchName, newBranchName string) error { |  | ||||||
| 	return checkoutNewBranch(repo.RepoPath(), repo.LocalRepoPath(), oldBranchName, newBranchName) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func checkoutNewBranch(repoPath, localPath, oldBranch, newBranch string) error { |  | ||||||
| 	if !com.IsExist(localPath) { |  | ||||||
| 		if err := updateLocalCopy(repoPath, localPath, oldBranch); err != nil { |  | ||||||
| 			return err |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	if err := git.Checkout(localPath, git.CheckoutOptions{ |  | ||||||
| 		Branch:    newBranch, |  | ||||||
| 		OldBranch: oldBranch, |  | ||||||
| 		Timeout:   time.Duration(setting.Git.Timeout.Pull) * time.Second, |  | ||||||
| 	}); err != nil { |  | ||||||
| 		return fmt.Errorf("Checkout New Branch: %v", err) |  | ||||||
| 	} |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // updateRepoFile adds new file to repository. |  | ||||||
| func (repo *Repository) UpdateRepoFile(doer *User, oldBranchName, branchName, oldTreeName, treeName, content, message string, isNewFile bool) (err error) { |  | ||||||
| 	repoWorkingPool.CheckIn(com.ToStr(repo.ID)) |  | ||||||
| 	defer repoWorkingPool.CheckOut(com.ToStr(repo.ID)) |  | ||||||
|  |  | ||||||
| 	if err = repo.DiscardLocalRepoChanges(oldBranchName); err != nil { |  | ||||||
| 		return fmt.Errorf("discardLocalRepoChanges: %s - %v", oldBranchName, err) |  | ||||||
| 	} else if err = repo.UpdateLocalRepo(oldBranchName); err != nil { |  | ||||||
| 		return fmt.Errorf("UpdateLocalRepo: %s - %v", oldBranchName, err) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if oldBranchName != branchName { |  | ||||||
| 		if err := repo.CheckoutNewBranch(oldBranchName, branchName); err != nil { |  | ||||||
| 			return fmt.Errorf("CheckoutNewBranch: %s - %s: %v", oldBranchName, branchName, err) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	localPath := repo.LocalRepoPath() |  | ||||||
| 	filePath := path.Join(localPath, treeName) |  | ||||||
|  |  | ||||||
| 	if len(message) == 0 { |  | ||||||
| 		if isNewFile { |  | ||||||
| 			message = "Add '" + treeName + "'" |  | ||||||
| 		} else { |  | ||||||
| 			message = "Update '" + treeName + "'" |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	os.MkdirAll(filepath.Dir(filePath), os.ModePerm) |  | ||||||
|  |  | ||||||
| 	// If new file, make sure it doesn't exist; if old file, move if file name change |  | ||||||
| 	if isNewFile { |  | ||||||
| 		if com.IsExist(filePath) { |  | ||||||
| 			return ErrRepoFileAlreadyExist{filePath} |  | ||||||
| 		} |  | ||||||
| 	} else if oldTreeName != "" && treeName != "" && treeName != oldTreeName { |  | ||||||
| 		if err = git.MoveFile(localPath, oldTreeName, treeName); err != nil { |  | ||||||
| 			return fmt.Errorf("MoveFile: %v", err) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if err = ioutil.WriteFile(filePath, []byte(content), 0666); err != nil { |  | ||||||
| 		return fmt.Errorf("WriteFile: %v", err) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if err = git.AddChanges(localPath, true); err != nil { |  | ||||||
| 		return fmt.Errorf("AddChanges: %v", err) |  | ||||||
| 	} else if err = git.CommitChanges(localPath, message, doer.NewGitSig()); err != nil { |  | ||||||
| 		return fmt.Errorf("CommitChanges: %v", err) |  | ||||||
| 	} else if err = git.Push(localPath, "origin", branchName); err != nil { |  | ||||||
| 		return fmt.Errorf("Push: %v", err) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (repo *Repository) GetPreviewDiff(repoPath, branchName, treeName, text string, maxlines, maxchars, maxfiles int) (diff *Diff, err error) { |  | ||||||
| 	repoWorkingPool.CheckIn(com.ToStr(repo.ID)) |  | ||||||
| 	defer repoWorkingPool.CheckOut(com.ToStr(repo.ID)) |  | ||||||
|  |  | ||||||
| 	if err = repo.DiscardLocalRepoChanges(branchName); err != nil { |  | ||||||
| 		return nil, fmt.Errorf("discardLocalRepoChanges: %s - %v", branchName, err) |  | ||||||
| 	} else if err = repo.UpdateLocalRepo(branchName); err != nil { |  | ||||||
| 		return nil, fmt.Errorf("UpdateLocalRepo: %s - %v", branchName, err) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	localPath := repo.LocalRepoPath() |  | ||||||
| 	filePath := path.Join(localPath, treeName) |  | ||||||
|  |  | ||||||
| 	os.MkdirAll(filepath.Dir(filePath), os.ModePerm) |  | ||||||
|  |  | ||||||
| 	if err = ioutil.WriteFile(filePath, []byte(text), 0666); err != nil { |  | ||||||
| 		return nil, fmt.Errorf("WriteFile: %v", err) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	var cmd *exec.Cmd |  | ||||||
| 	cmd = exec.Command("git", "diff", treeName) |  | ||||||
| 	cmd.Dir = localPath |  | ||||||
| 	cmd.Stderr = os.Stderr |  | ||||||
|  |  | ||||||
| 	stdout, err := cmd.StdoutPipe() |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, fmt.Errorf("StdoutPipe: %v", err) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if err = cmd.Start(); err != nil { |  | ||||||
| 		return nil, fmt.Errorf("Start: %v", err) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	pid := process.Add(fmt.Sprintf("GetDiffRange (%s)", repoPath), cmd) |  | ||||||
| 	defer process.Remove(pid) |  | ||||||
|  |  | ||||||
| 	diff, err = ParsePatch(maxlines, maxchars, maxfiles, stdout) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, fmt.Errorf("ParsePatch: %v", err) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if err = cmd.Wait(); err != nil { |  | ||||||
| 		return nil, fmt.Errorf("Wait: %v", err) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return diff, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // ________         .__          __           ___________.__.__ | // ________         .__          __           ___________.__.__ | ||||||
| // \______ \   ____ |  |   _____/  |_  ____   \_   _____/|__|  |   ____ | // \______ \   ____ |  |   _____/  |_  ____   \_   _____/|__|  |   ____ | ||||||
| //  |    |  \_/ __ \|  | _/ __ \   __\/ __ \   |    __)  |  |  | _/ __ \ | //  |    |  \_/ __ \|  | _/ __ \   __\/ __ \   |    __)  |  |  | _/ __ \ | ||||||
| @@ -2412,11 +2252,11 @@ func (repo *Repository) DeleteRepoFile(doer *User, branch, treeName, message str | |||||||
| 	repoWorkingPool.CheckIn(com.ToStr(repo.ID)) | 	repoWorkingPool.CheckIn(com.ToStr(repo.ID)) | ||||||
| 	defer repoWorkingPool.CheckOut(com.ToStr(repo.ID)) | 	defer repoWorkingPool.CheckOut(com.ToStr(repo.ID)) | ||||||
|  |  | ||||||
| 	localPath := repo.LocalRepoPath() | 	localPath := repo.LocalCopyPath() | ||||||
| 	if err = discardLocalRepoChanges(localPath, branch); err != nil { | 	if err = discardLocalRepoBranchChanges(localPath, branch); err != nil { | ||||||
| 		return fmt.Errorf("discardLocalRepoChanges: %v", err) | 		return fmt.Errorf("discardLocalRepoChanges: %v", err) | ||||||
| 	} else if err = repo.UpdateLocalRepo(branch); err != nil { | 	} else if err = repo.UpdateLocalCopyBranch(branch); err != nil { | ||||||
| 		return fmt.Errorf("UpdateLocalRepo: %v", err) | 		return fmt.Errorf("UpdateLocalCopyBranch: %v", err) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	filePath := path.Join(localPath, treeName) | 	filePath := path.Join(localPath, treeName) | ||||||
| @@ -2450,12 +2290,12 @@ func (repo *Repository) UploadRepoFiles(doer *User, oldBranchName, branchName, t | |||||||
| 	repoWorkingPool.CheckIn(com.ToStr(repo.ID)) | 	repoWorkingPool.CheckIn(com.ToStr(repo.ID)) | ||||||
| 	defer repoWorkingPool.CheckOut(com.ToStr(repo.ID)) | 	defer repoWorkingPool.CheckOut(com.ToStr(repo.ID)) | ||||||
|  |  | ||||||
| 	localPath := repo.LocalRepoPath() | 	localPath := repo.LocalCopyPath() | ||||||
|  |  | ||||||
| 	if err = discardLocalRepoChanges(localPath, oldBranchName); err != nil { | 	if err = discardLocalRepoBranchChanges(localPath, oldBranchName); err != nil { | ||||||
| 		return fmt.Errorf("discardLocalRepoChanges: %v", err) | 		return fmt.Errorf("discardLocalRepoChanges: %v", err) | ||||||
| 	} else if err = repo.UpdateLocalRepo(oldBranchName); err != nil { | 	} else if err = repo.UpdateLocalCopyBranch(oldBranchName); err != nil { | ||||||
| 		return fmt.Errorf("UpdateLocalRepo: %v", err) | 		return fmt.Errorf("UpdateLocalCopyBranch: %v", err) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if oldBranchName != branchName { | 	if oldBranchName != branchName { | ||||||
| @@ -2637,12 +2477,12 @@ func (repo *Repository) CreateNewBranch(doer *User, oldBranchName, branchName st | |||||||
| 	repoWorkingPool.CheckIn(com.ToStr(repo.ID)) | 	repoWorkingPool.CheckIn(com.ToStr(repo.ID)) | ||||||
| 	defer repoWorkingPool.CheckOut(com.ToStr(repo.ID)) | 	defer repoWorkingPool.CheckOut(com.ToStr(repo.ID)) | ||||||
|  |  | ||||||
| 	localPath := repo.LocalRepoPath() | 	localPath := repo.LocalCopyPath() | ||||||
|  |  | ||||||
| 	if err = discardLocalRepoChanges(localPath, oldBranchName); err != nil { | 	if err = discardLocalRepoBranchChanges(localPath, oldBranchName); err != nil { | ||||||
| 		return fmt.Errorf("discardLocalRepoChanges: %v", err) | 		return fmt.Errorf("discardLocalRepoChanges: %v", err) | ||||||
| 	} else if err = repo.UpdateLocalRepo(oldBranchName); err != nil { | 	} else if err = repo.UpdateLocalCopyBranch(oldBranchName); err != nil { | ||||||
| 		return fmt.Errorf("UpdateLocalRepo: %v", err) | 		return fmt.Errorf("UpdateLocalCopyBranch: %v", err) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if err = repo.CheckoutNewBranch(oldBranchName, branchName); err != nil { | 	if err = repo.CheckoutNewBranch(oldBranchName, branchName); err != nil { | ||||||
|   | |||||||
							
								
								
									
										211
									
								
								models/repo_editor.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										211
									
								
								models/repo_editor.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,211 @@ | |||||||
|  | // Copyright 2016 The Gogs Authors. All rights reserved. | ||||||
|  | // Use of this source code is governed by a MIT-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | package models | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"io/ioutil" | ||||||
|  | 	"os" | ||||||
|  | 	"os/exec" | ||||||
|  | 	"path" | ||||||
|  | 	"path/filepath" | ||||||
|  | 	"time" | ||||||
|  |  | ||||||
|  | 	"github.com/Unknwon/com" | ||||||
|  |  | ||||||
|  | 	git "github.com/gogits/git-module" | ||||||
|  |  | ||||||
|  | 	"github.com/gogits/gogs/modules/log" | ||||||
|  | 	"github.com/gogits/gogs/modules/process" | ||||||
|  | 	"github.com/gogits/gogs/modules/setting" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // ___________    .___.__  __    ___________.__.__ | ||||||
|  | // \_   _____/  __| _/|__|/  |_  \_   _____/|__|  |   ____ | ||||||
|  | //  |    __)_  / __ | |  \   __\  |    __)  |  |  | _/ __ \ | ||||||
|  | //  |        \/ /_/ | |  ||  |    |     \   |  |  |_\  ___/ | ||||||
|  | // /_______  /\____ | |__||__|    \___  /   |__|____/\___  > | ||||||
|  | //         \/      \/                 \/                 \/ | ||||||
|  |  | ||||||
|  | // discardLocalRepoBranchChanges discards local commits of given branch | ||||||
|  | // to make sure it is even to remote branch when local copy exists. | ||||||
|  | func discardLocalRepoBranchChanges(localPath, branch string) error { | ||||||
|  | 	if !com.IsExist(localPath) { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 	// No need to check if nothing in the repository. | ||||||
|  | 	if !git.IsBranchExist(localPath, branch) { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 	if err := git.ResetHEAD(localPath, true, "origin/"+branch); err != nil { | ||||||
|  | 		return fmt.Errorf("ResetHEAD: %v", err) | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (repo *Repository) DiscardLocalRepoBranchChanges(branch string) error { | ||||||
|  | 	return discardLocalRepoBranchChanges(repo.LocalCopyPath(), branch) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func checkoutNewBranch(repoPath, localPath, oldBranch, newBranch string) error { | ||||||
|  | 	if !com.IsExist(localPath) { | ||||||
|  | 		if err := UpdateLocalCopyBranch(repoPath, localPath, oldBranch); err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if err := git.Checkout(localPath, git.CheckoutOptions{ | ||||||
|  | 		Branch:    newBranch, | ||||||
|  | 		OldBranch: oldBranch, | ||||||
|  | 		Timeout:   time.Duration(setting.Git.Timeout.Pull) * time.Second, | ||||||
|  | 	}); err != nil { | ||||||
|  | 		return fmt.Errorf("Checkout: %v", err) | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // CheckoutNewBranch checks out a new branch from the given branch name. | ||||||
|  | func (repo *Repository) CheckoutNewBranch(oldBranch, newBranch string) error { | ||||||
|  | 	return checkoutNewBranch(repo.RepoPath(), repo.LocalCopyPath(), oldBranch, newBranch) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type UpdateRepoFileOptions struct { | ||||||
|  | 	LastCommitID string | ||||||
|  | 	OldBranch    string | ||||||
|  | 	NewBranch    string | ||||||
|  | 	OldTreeName  string | ||||||
|  | 	NewTreeName  string | ||||||
|  | 	Message      string | ||||||
|  | 	Content      string | ||||||
|  | 	IsNewFile    bool | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // updateRepoFile adds new file to repository. | ||||||
|  | func (repo *Repository) UpdateRepoFile(doer *User, opts *UpdateRepoFileOptions) (err error) { | ||||||
|  | 	repoWorkingPool.CheckIn(com.ToStr(repo.ID)) | ||||||
|  | 	defer repoWorkingPool.CheckOut(com.ToStr(repo.ID)) | ||||||
|  |  | ||||||
|  | 	if err = repo.DiscardLocalRepoBranchChanges(opts.OldBranch); err != nil { | ||||||
|  | 		return fmt.Errorf("DiscardLocalRepoBranchChanges [branch: %s]: %v", opts.OldBranch, err) | ||||||
|  | 	} else if err = repo.UpdateLocalCopyBranch(opts.OldBranch); err != nil { | ||||||
|  | 		return fmt.Errorf("UpdateLocalCopyBranch [branch: %s]: %v", opts.OldBranch, err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if opts.OldBranch != opts.NewBranch { | ||||||
|  | 		if err := repo.CheckoutNewBranch(opts.OldBranch, opts.NewBranch); err != nil { | ||||||
|  | 			return fmt.Errorf("CheckoutNewBranch [old_branch: %s, new_branch: %s]: %v", opts.OldBranch, opts.NewBranch, err) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	localPath := repo.LocalCopyPath() | ||||||
|  | 	filePath := path.Join(localPath, opts.NewTreeName) | ||||||
|  |  | ||||||
|  | 	if len(opts.Message) == 0 { | ||||||
|  | 		if opts.IsNewFile { | ||||||
|  | 			opts.Message = "Add '" + opts.NewTreeName + "'" | ||||||
|  | 		} else { | ||||||
|  | 			opts.Message = "Update '" + opts.NewTreeName + "'" | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	os.MkdirAll(path.Dir(filePath), os.ModePerm) | ||||||
|  |  | ||||||
|  | 	// If new file, make sure it doesn't exist; if old file, move if file name change. | ||||||
|  | 	if opts.IsNewFile { | ||||||
|  | 		if com.IsExist(filePath) { | ||||||
|  | 			return ErrRepoFileAlreadyExist{filePath} | ||||||
|  | 		} | ||||||
|  | 	} else if len(opts.OldTreeName) > 0 && len(opts.NewTreeName) > 0 && opts.NewTreeName != opts.OldTreeName { | ||||||
|  | 		if err = git.MoveFile(localPath, opts.OldTreeName, opts.NewTreeName); err != nil { | ||||||
|  | 			return fmt.Errorf("MoveFile [old_tree_name: %s, new_tree_name: %s]: %v", opts.OldTreeName, opts.NewTreeName, err) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if err = ioutil.WriteFile(filePath, []byte(opts.Content), 0666); err != nil { | ||||||
|  | 		return fmt.Errorf("WriteFile: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if err = git.AddChanges(localPath, true); err != nil { | ||||||
|  | 		return fmt.Errorf("AddChanges: %v", err) | ||||||
|  | 	} else if err = git.CommitChanges(localPath, opts.Message, doer.NewGitSig()); err != nil { | ||||||
|  | 		return fmt.Errorf("CommitChanges: %v", err) | ||||||
|  | 	} else if err = git.Push(localPath, "origin", opts.NewBranch); err != nil { | ||||||
|  | 		return fmt.Errorf("Push: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	gitRepo, err := git.OpenRepository(repo.RepoPath()) | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Error(4, "OpenRepository: %v", err) | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 	commit, err := gitRepo.GetBranchCommit(opts.NewBranch) | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Error(4, "GetBranchCommit [branch: %s]: %v", opts.NewBranch, err) | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	pushCommits := &PushCommits{ | ||||||
|  | 		Len:     1, | ||||||
|  | 		Commits: []*PushCommit{CommitToPushCommit(commit)}, | ||||||
|  | 	} | ||||||
|  | 	oldCommitID := opts.LastCommitID | ||||||
|  | 	if opts.NewBranch != opts.OldBranch { | ||||||
|  | 		oldCommitID = "0000000000000000000000000000000000000000" // New Branch so we use all 0s | ||||||
|  | 	} | ||||||
|  | 	if err := CommitRepoAction(doer.ID, repo.MustOwner().ID, doer.Name, doer.Email, | ||||||
|  | 		repo.ID, repo.MustOwner().Name, repo.Name, git.BRANCH_PREFIX+opts.NewBranch, | ||||||
|  | 		pushCommits, oldCommitID, commit.ID.String()); err != nil { | ||||||
|  | 		log.Error(4, "CommitRepoAction: %v", err) | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 	go HookQueue.Add(repo.ID) | ||||||
|  |  | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (repo *Repository) GetDiffPreview(branch, treeName, content string) (diff *Diff, err error) { | ||||||
|  | 	repoWorkingPool.CheckIn(com.ToStr(repo.ID)) | ||||||
|  | 	defer repoWorkingPool.CheckOut(com.ToStr(repo.ID)) | ||||||
|  |  | ||||||
|  | 	if err = repo.DiscardLocalRepoBranchChanges(branch); err != nil { | ||||||
|  | 		return nil, fmt.Errorf("discardLocalRepoChanges: %s - %v", branch, err) | ||||||
|  | 	} else if err = repo.UpdateLocalCopyBranch(branch); err != nil { | ||||||
|  | 		return nil, fmt.Errorf("UpdateLocalCopyBranch: %s - %v", branch, err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	localPath := repo.LocalCopyPath() | ||||||
|  | 	filePath := path.Join(localPath, treeName) | ||||||
|  |  | ||||||
|  | 	os.MkdirAll(filepath.Dir(filePath), os.ModePerm) | ||||||
|  | 	if err = ioutil.WriteFile(filePath, []byte(content), 0666); err != nil { | ||||||
|  | 		return nil, fmt.Errorf("WriteFile: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	cmd := exec.Command("git", "diff", treeName) | ||||||
|  | 	cmd.Dir = localPath | ||||||
|  | 	cmd.Stderr = os.Stderr | ||||||
|  |  | ||||||
|  | 	stdout, err := cmd.StdoutPipe() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, fmt.Errorf("StdoutPipe: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if err = cmd.Start(); err != nil { | ||||||
|  | 		return nil, fmt.Errorf("Start: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	pid := process.Add(fmt.Sprintf("GetDiffRange [repo_path: %s]", repo.RepoPath()), cmd) | ||||||
|  | 	defer process.Remove(pid) | ||||||
|  |  | ||||||
|  | 	diff, err = ParsePatch(setting.Git.MaxGitDiffLines, setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles, stdout) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, fmt.Errorf("ParsePatch: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if err = cmd.Wait(); err != nil { | ||||||
|  | 		return nil, fmt.Errorf("Wait: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return diff, nil | ||||||
|  | } | ||||||
| @@ -76,24 +76,11 @@ func (repo *Repository) LocalWikiPath() string { | |||||||
|  |  | ||||||
| // UpdateLocalWiki makes sure the local copy of repository wiki is up-to-date. | // UpdateLocalWiki makes sure the local copy of repository wiki is up-to-date. | ||||||
| func (repo *Repository) UpdateLocalWiki() error { | func (repo *Repository) UpdateLocalWiki() error { | ||||||
| 	return updateLocalCopy(repo.WikiPath(), repo.LocalWikiPath(), "") | 	return UpdateLocalCopyBranch(repo.WikiPath(), repo.LocalWikiPath(), "master") | ||||||
| } | } | ||||||
|  |  | ||||||
| // discardLocalWikiChanges discards local commits make sure |  | ||||||
| // it is even to remote branch when local copy exists. |  | ||||||
| func discardLocalWikiChanges(localPath string) error { | func discardLocalWikiChanges(localPath string) error { | ||||||
| 	if !com.IsExist(localPath) { | 	return discardLocalRepoBranchChanges(localPath, "master") | ||||||
| 		return nil |  | ||||||
| 	} |  | ||||||
| 	// No need to check if nothing in the repository. |  | ||||||
| 	if !git.IsBranchExist(localPath, "master") { |  | ||||||
| 		return nil |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if err := git.ResetHEAD(localPath, true, "origin/master"); err != nil { |  | ||||||
| 		return fmt.Errorf("ResetHEAD: %v", err) |  | ||||||
| 	} |  | ||||||
| 	return nil |  | ||||||
| } | } | ||||||
|  |  | ||||||
| // updateWikiPage adds new page to repository wiki. | // updateWikiPage adds new page to repository wiki. | ||||||
|   | |||||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| @@ -40,14 +40,14 @@ func NewBranchPost(ctx *context.Context, form auth.NewBranchForm) { | |||||||
| 	branchName := form.BranchName | 	branchName := form.BranchName | ||||||
|  |  | ||||||
| 	if ctx.HasError() || !ctx.Repo.IsWriter() || branchName == oldBranchName { | 	if ctx.HasError() || !ctx.Repo.IsWriter() || branchName == oldBranchName { | ||||||
| 		ctx.Redirect(EscapeUrl(ctx.Repo.RepoLink + "/src/" + oldBranchName)) | 		ctx.Redirect(ctx.Repo.RepoLink + "/src/" + oldBranchName) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	branchName = url.QueryEscape(strings.Replace(strings.Trim(branchName, " "), " ", "-", -1)) | 	branchName = url.QueryEscape(strings.Replace(strings.Trim(branchName, " "), " ", "-", -1)) | ||||||
|  |  | ||||||
| 	if _, err := ctx.Repo.Repository.GetBranch(branchName); err == nil { | 	if _, err := ctx.Repo.Repository.GetBranch(branchName); err == nil { | ||||||
| 		ctx.Redirect(EscapeUrl(ctx.Repo.RepoLink + "/src/" + branchName)) | 		ctx.Redirect(ctx.Repo.RepoLink + "/src/" + branchName) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @@ -77,5 +77,5 @@ func NewBranchPost(ctx *context.Context, form auth.NewBranchForm) { | |||||||
| 		models.HookQueue.Add(ctx.Repo.Repository.ID) | 		models.HookQueue.Add(ctx.Repo.Repository.ID) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	ctx.Redirect(EscapeUrl(ctx.Repo.RepoLink + "/src/" + branchName)) | 	ctx.Redirect(ctx.Repo.RepoLink + "/src/" + branchName) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -20,28 +20,24 @@ import ( | |||||||
| ) | ) | ||||||
|  |  | ||||||
| const ( | const ( | ||||||
| 	EDIT             base.TplName = "repo/edit" | 	EDIT             base.TplName = "repo/editor/edit" | ||||||
| 	DIFF_PREVIEW     base.TplName = "repo/diff_preview" | 	DIFF_PREVIEW     base.TplName = "repo/editor/diff_preview" | ||||||
| 	DIFF_PREVIEW_NEW base.TplName = "repo/diff_preview_new" | 	DIFF_PREVIEW_NEW base.TplName = "repo/editor/diff_preview_new" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func EditFile(ctx *context.Context) { |  | ||||||
| 	editFile(ctx, false) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func NewFile(ctx *context.Context) { |  | ||||||
| 	editFile(ctx, true) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func editFile(ctx *context.Context, isNewFile bool) { | func editFile(ctx *context.Context, isNewFile bool) { | ||||||
|  | 	// Don't allow edit a file in a specific commit. | ||||||
|  | 	if ctx.Repo.IsViewCommit { | ||||||
|  | 		ctx.Handle(404, "", nil) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	ctx.Data["PageIsEdit"] = true | 	ctx.Data["PageIsEdit"] = true | ||||||
| 	ctx.Data["IsNewFile"] = isNewFile | 	ctx.Data["IsNewFile"] = isNewFile | ||||||
| 	ctx.Data["RequireHighlightJS"] = true | 	ctx.Data["RequireHighlightJS"] = true | ||||||
|  | 	ctx.Data["RequireSimpleMDE"] = true | ||||||
|  |  | ||||||
| 	userName := ctx.Repo.Owner.Name | 	branchLink := ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchName | ||||||
| 	repoName := ctx.Repo.Repository.Name |  | ||||||
| 	branchName := ctx.Repo.BranchName |  | ||||||
| 	branchLink := ctx.Repo.RepoLink + "/src/" + branchName |  | ||||||
| 	treeName := ctx.Repo.TreeName | 	treeName := ctx.Repo.TreeName | ||||||
|  |  | ||||||
| 	var treeNames []string | 	var treeNames []string | ||||||
| @@ -51,19 +47,22 @@ func editFile(ctx *context.Context, isNewFile bool) { | |||||||
|  |  | ||||||
| 	if !isNewFile { | 	if !isNewFile { | ||||||
| 		entry, err := ctx.Repo.Commit.GetTreeEntryByPath(treeName) | 		entry, err := ctx.Repo.Commit.GetTreeEntryByPath(treeName) | ||||||
|  | 		if err != nil { | ||||||
| 		if err != nil && git.IsErrNotExist(err) { | 			if git.IsErrNotExist(err) { | ||||||
| 			ctx.Handle(404, "GetTreeEntryByPath", err) | 				ctx.Handle(404, "GetTreeEntryByPath", err) | ||||||
|  | 			} else { | ||||||
|  | 				ctx.Handle(500, "GetTreeEntryByPath", err) | ||||||
|  | 			} | ||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		if (ctx.Repo.IsViewCommit) || entry == nil || entry.IsDir() { | 		// No way to edit a directory online. | ||||||
| 			ctx.Handle(404, "repo.Home", nil) | 		if entry.IsDir() { | ||||||
|  | 			ctx.Handle(404, "", nil) | ||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		blob := entry.Blob() | 		blob := entry.Blob() | ||||||
|  |  | ||||||
| 		dataRc, err := blob.Data() | 		dataRc, err := blob.Data() | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			ctx.Handle(404, "blob.Data", err) | 			ctx.Handle(404, "blob.Data", err) | ||||||
| @@ -79,16 +78,15 @@ func editFile(ctx *context.Context, isNewFile bool) { | |||||||
| 			buf = buf[:n] | 			buf = buf[:n] | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | 		// Only text file are editable online. | ||||||
| 		_, isTextFile := base.IsTextFile(buf) | 		_, isTextFile := base.IsTextFile(buf) | ||||||
|  |  | ||||||
| 		if !isTextFile { | 		if !isTextFile { | ||||||
| 			ctx.Handle(404, "repo.Home", nil) | 			ctx.Handle(404, "", nil) | ||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		d, _ := ioutil.ReadAll(dataRc) | 		d, _ := ioutil.ReadAll(dataRc) | ||||||
| 		buf = append(buf, d...) | 		buf = append(buf, d...) | ||||||
|  |  | ||||||
| 		if err, content := template.ToUTF8WithErr(buf); err != nil { | 		if err, content := template.ToUTF8WithErr(buf); err != nil { | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				log.Error(4, "Convert content encoding: %s", err) | 				log.Error(4, "Convert content encoding: %s", err) | ||||||
| @@ -98,47 +96,38 @@ func editFile(ctx *context.Context, isNewFile bool) { | |||||||
| 			ctx.Data["FileContent"] = content | 			ctx.Data["FileContent"] = content | ||||||
| 		} | 		} | ||||||
| 	} else { | 	} else { | ||||||
| 		treeNames = append(treeNames, "") | 		treeNames = append(treeNames, "") // Append empty string to allow user name the new file. | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	ctx.Data["RequireSimpleMDE"] = true |  | ||||||
|  |  | ||||||
| 	ctx.Data["UserName"] = userName |  | ||||||
| 	ctx.Data["RepoName"] = repoName |  | ||||||
| 	ctx.Data["BranchName"] = branchName |  | ||||||
| 	ctx.Data["TreeName"] = treeName | 	ctx.Data["TreeName"] = treeName | ||||||
| 	ctx.Data["TreeNames"] = treeNames | 	ctx.Data["TreeNames"] = treeNames | ||||||
| 	ctx.Data["BranchLink"] = branchLink | 	ctx.Data["BranchLink"] = branchLink | ||||||
| 	ctx.Data["CommitSummary"] = "" | 	ctx.Data["commit_summary"] = "" | ||||||
| 	ctx.Data["CommitMessage"] = "" | 	ctx.Data["commit_message"] = "" | ||||||
| 	ctx.Data["CommitChoice"] = "direct" | 	ctx.Data["commit_choice"] = "direct" | ||||||
| 	ctx.Data["NewBranchName"] = "" | 	ctx.Data["new_branch_name"] = "" | ||||||
| 	ctx.Data["CommitDirectlyToThisBranch"] = ctx.Tr("repo.commit_directly_to_this_branch", "<strong class=\"branch-name\">"+branchName+"</strong>") | 	ctx.Data["last_commit"] = ctx.Repo.Commit.ID | ||||||
| 	ctx.Data["CreateNewBranch"] = ctx.Tr("repo.create_new_branch", "<strong>"+ctx.Tr("repo.new_branch")+"</strong>") |  | ||||||
| 	ctx.Data["LastCommit"] = ctx.Repo.Commit.ID |  | ||||||
| 	ctx.Data["MarkdownFileExts"] = strings.Join(setting.Markdown.FileExtensions, ",") | 	ctx.Data["MarkdownFileExts"] = strings.Join(setting.Markdown.FileExtensions, ",") | ||||||
| 	ctx.Data["LineWrapExtensions"] = strings.Join(setting.Repository.Editor.LineWrapExtensions, ",") | 	ctx.Data["LineWrapExtensions"] = strings.Join(setting.Repository.Editor.LineWrapExtensions, ",") | ||||||
| 	ctx.Data["PreviewableFileModes"] = strings.Join(setting.Repository.Editor.PreviewableFileModes, ",") | 	ctx.Data["PreviewableFileModes"] = strings.Join(setting.Repository.Editor.PreviewableFileModes, ",") | ||||||
| 	ctx.Data["PreviewDiffURL"] = ctx.Repo.RepoLink + "/preview/" + branchName + "/" + treeName |  | ||||||
|  |  | ||||||
| 	ctx.HTML(200, EDIT) | 	ctx.HTML(200, EDIT) | ||||||
| } | } | ||||||
|  |  | ||||||
| func EditFilePost(ctx *context.Context, form auth.EditRepoFileForm) { | func EditFile(ctx *context.Context) { | ||||||
| 	editFilePost(ctx, form, false) | 	editFile(ctx, false) | ||||||
| } | } | ||||||
|  |  | ||||||
| func NewFilePost(ctx *context.Context, form auth.EditRepoFileForm) { | func NewFile(ctx *context.Context) { | ||||||
| 	editFilePost(ctx, form, true) | 	editFile(ctx, true) | ||||||
| } | } | ||||||
|  |  | ||||||
| func editFilePost(ctx *context.Context, form auth.EditRepoFileForm, isNewFile bool) { | func editFilePost(ctx *context.Context, form auth.EditRepoFileForm, isNewFile bool) { | ||||||
| 	ctx.Data["PageIsEdit"] = true | 	ctx.Data["PageIsEdit"] = true | ||||||
| 	ctx.Data["IsNewFile"] = isNewFile | 	ctx.Data["IsNewFile"] = isNewFile | ||||||
| 	ctx.Data["RequireHighlightJS"] = true | 	ctx.Data["RequireHighlightJS"] = true | ||||||
|  | 	ctx.Data["RequireSimpleMDE"] = true | ||||||
|  |  | ||||||
| 	userName := ctx.Repo.Owner.Name |  | ||||||
| 	repoName := ctx.Repo.Repository.Name |  | ||||||
| 	oldBranchName := ctx.Repo.BranchName | 	oldBranchName := ctx.Repo.BranchName | ||||||
| 	branchName := oldBranchName | 	branchName := oldBranchName | ||||||
| 	branchLink := ctx.Repo.RepoLink + "/src/" + branchName | 	branchLink := ctx.Repo.RepoLink + "/src/" + branchName | ||||||
| @@ -160,26 +149,18 @@ func editFilePost(ctx *context.Context, form auth.EditRepoFileForm, isNewFile bo | |||||||
| 		treeNames = strings.Split(treeName, "/") | 		treeNames = strings.Split(treeName, "/") | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	ctx.Data["RequireSimpleMDE"] = true |  | ||||||
|  |  | ||||||
| 	ctx.Data["UserName"] = userName |  | ||||||
| 	ctx.Data["RepoName"] = repoName |  | ||||||
| 	ctx.Data["BranchName"] = branchName |  | ||||||
| 	ctx.Data["TreeName"] = treeName | 	ctx.Data["TreeName"] = treeName | ||||||
| 	ctx.Data["TreeNames"] = treeNames | 	ctx.Data["TreeNames"] = treeNames | ||||||
| 	ctx.Data["BranchLink"] = branchLink | 	ctx.Data["BranchLink"] = branchLink | ||||||
| 	ctx.Data["FileContent"] = content | 	ctx.Data["FileContent"] = content | ||||||
| 	ctx.Data["CommitSummary"] = form.CommitSummary | 	ctx.Data["commit_summary"] = form.CommitSummary | ||||||
| 	ctx.Data["CommitMessage"] = form.CommitMessage | 	ctx.Data["commit_message"] = form.CommitMessage | ||||||
| 	ctx.Data["CommitChoice"] = commitChoice | 	ctx.Data["commit_choice"] = commitChoice | ||||||
| 	ctx.Data["NewBranchName"] = branchName | 	ctx.Data["new_branch_name"] = branchName | ||||||
| 	ctx.Data["CommitDirectlyToThisBranch"] = ctx.Tr("repo.commit_directly_to_this_branch", "<strong class=\"branch-name\">"+oldBranchName+"</strong>") | 	ctx.Data["last_commit"] = ctx.Repo.Commit.ID | ||||||
| 	ctx.Data["CreateNewBranch"] = ctx.Tr("repo.create_new_branch", "<strong>"+ctx.Tr("repo.new_branch")+"</strong>") |  | ||||||
| 	ctx.Data["LastCommit"] = ctx.Repo.Commit.ID |  | ||||||
| 	ctx.Data["MarkdownFileExts"] = strings.Join(setting.Markdown.FileExtensions, ",") | 	ctx.Data["MarkdownFileExts"] = strings.Join(setting.Markdown.FileExtensions, ",") | ||||||
| 	ctx.Data["LineWrapExtensions"] = strings.Join(setting.Repository.Editor.LineWrapExtensions, ",") | 	ctx.Data["LineWrapExtensions"] = strings.Join(setting.Repository.Editor.LineWrapExtensions, ",") | ||||||
| 	ctx.Data["PreviewableFileModes"] = strings.Join(setting.Repository.Editor.PreviewableFileModes, ",") | 	ctx.Data["PreviewableFileModes"] = strings.Join(setting.Repository.Editor.PreviewableFileModes, ",") | ||||||
| 	ctx.Data["PreviewDiffURL"] = ctx.Repo.RepoLink + "/preview/" + branchName + "/" + treeName |  | ||||||
|  |  | ||||||
| 	if ctx.HasError() { | 	if ctx.HasError() { | ||||||
| 		ctx.HTML(200, EDIT) | 		ctx.HTML(200, EDIT) | ||||||
| @@ -188,41 +169,41 @@ func editFilePost(ctx *context.Context, form auth.EditRepoFileForm, isNewFile bo | |||||||
|  |  | ||||||
| 	if len(treeName) == 0 { | 	if len(treeName) == 0 { | ||||||
| 		ctx.Data["Err_Filename"] = true | 		ctx.Data["Err_Filename"] = true | ||||||
| 		ctx.RenderWithErr(ctx.Tr("repo.filename_cannot_be_empty"), EDIT, &form) | 		ctx.RenderWithErr(ctx.Tr("repo.editor.filename_cannot_be_empty"), EDIT, &form) | ||||||
| 		log.Error(4, "%s: %s", "EditFile", "Filename can't be empty") |  | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if oldBranchName != branchName { | 	if oldBranchName != branchName { | ||||||
| 		if _, err := ctx.Repo.Repository.GetBranch(branchName); err == nil { | 		if _, err := ctx.Repo.Repository.GetBranch(branchName); err == nil { | ||||||
| 			ctx.Data["Err_Branchname"] = true | 			ctx.Data["Err_Branchname"] = true | ||||||
| 			ctx.RenderWithErr(ctx.Tr("repo.branch_already_exists"), EDIT, &form) | 			ctx.RenderWithErr(ctx.Tr("repo.editor.branch_already_exists", branchName), EDIT, &form) | ||||||
| 			log.Error(4, "%s: %s - %s", "BranchName", branchName, "Branch already exists") |  | ||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	treepath := "" | 	var treepath string | ||||||
| 	for index, part := range treeNames { | 	for index, part := range treeNames { | ||||||
| 		treepath = path.Join(treepath, part) | 		treepath = path.Join(treepath, part) | ||||||
| 		entry, err := ctx.Repo.Commit.GetTreeEntryByPath(treepath) | 		entry, err := ctx.Repo.Commit.GetTreeEntryByPath(treepath) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			// Means there is no item with that name, so we're good | 			if git.IsErrNotExist(err) { | ||||||
| 			break | 				// Means there is no item with that name, so we're good | ||||||
|  | 				break | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			ctx.Handle(500, "GetTreeEntryByPath", err) | ||||||
|  | 			return | ||||||
| 		} | 		} | ||||||
| 		if index != len(treeNames)-1 { | 		if index != len(treeNames)-1 { | ||||||
| 			if !entry.IsDir() { | 			if !entry.IsDir() { | ||||||
| 				ctx.Data["Err_Filename"] = true | 				ctx.Data["Err_Filename"] = true | ||||||
| 				ctx.RenderWithErr(ctx.Tr("repo.directory_is_a_file"), EDIT, &form) | 				ctx.RenderWithErr(ctx.Tr("repo.editor.directory_is_a_file", part), EDIT, &form) | ||||||
| 				log.Error(4, "%s: %s - %s", "EditFile", treeName, "Directory given is a file") |  | ||||||
| 				return | 				return | ||||||
| 			} | 			} | ||||||
| 		} else { | 		} else { | ||||||
| 			if entry.IsDir() { | 			if entry.IsDir() { | ||||||
| 				ctx.Data["Err_Filename"] = true | 				ctx.Data["Err_Filename"] = true | ||||||
| 				ctx.RenderWithErr(ctx.Tr("repo.filename_is_a_directory"), EDIT, &form) | 				ctx.RenderWithErr(ctx.Tr("repo.editor.filename_is_a_directory", part), EDIT, &form) | ||||||
| 				log.Error(4, "%s: %s - %s", "EditFile", treeName, "Filename given is a dirctory") |  | ||||||
| 				return | 				return | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| @@ -230,125 +211,119 @@ func editFilePost(ctx *context.Context, form auth.EditRepoFileForm, isNewFile bo | |||||||
|  |  | ||||||
| 	if !isNewFile { | 	if !isNewFile { | ||||||
| 		_, err := ctx.Repo.Commit.GetTreeEntryByPath(oldTreeName) | 		_, err := ctx.Repo.Commit.GetTreeEntryByPath(oldTreeName) | ||||||
| 		if err != nil && git.IsErrNotExist(err) { | 		if err != nil { | ||||||
| 			ctx.Data["Err_Filename"] = true | 			if git.IsErrNotExist(err) { | ||||||
| 			ctx.RenderWithErr(ctx.Tr("repo.file_editing_no_longer_exists"), EDIT, &form) | 				ctx.Data["Err_Filename"] = true | ||||||
| 			log.Error(4, "%s: %s / %s - %s", "EditFile", branchName, oldTreeName, "File doesn't exist for editing") | 				ctx.RenderWithErr(ctx.Tr("repo.editor.file_editing_no_longer_exists", oldTreeName), EDIT, &form) | ||||||
|  | 			} else { | ||||||
|  | 				ctx.Handle(500, "GetTreeEntryByPath", err) | ||||||
|  | 			} | ||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
| 		if lastCommit != ctx.Repo.CommitID { | 		if lastCommit != ctx.Repo.CommitID { | ||||||
| 			if files, err := ctx.Repo.Commit.GetFilesChangedSinceCommit(lastCommit); err == nil { | 			files, err := ctx.Repo.Commit.GetFilesChangedSinceCommit(lastCommit) | ||||||
| 				for _, file := range files { | 			if err != nil { | ||||||
| 					if file == treeName { | 				ctx.Handle(500, "GetFilesChangedSinceCommit", err) | ||||||
| 						name := ctx.Repo.Commit.Author.Name | 				return | ||||||
| 						if u, err := models.GetUserByEmail(ctx.Repo.Commit.Author.Email); err == nil { | 			} | ||||||
| 							name = `<a href="` + setting.AppSubUrl + "/" + u.Name + `" target="_blank">` + u.Name + `</a>` |  | ||||||
| 						} | 			for _, file := range files { | ||||||
| 						message := ctx.Tr("repo.user_has_committed_since_you_started_editing", name) + | 				if file == treeName { | ||||||
| 							` <a href="` + ctx.Repo.RepoLink + "/commit/" + ctx.Repo.CommitID + `" target="_blank">` + ctx.Tr("repo.see_what_changed") + `</a>` + | 					ctx.RenderWithErr(ctx.Tr("repo.editor.file_changed_while_editing", ctx.Repo.RepoLink+"/compare/"+lastCommit+"..."+ctx.Repo.CommitID), EDIT, &form) | ||||||
| 							" " + ctx.Tr("repo.pressing_commit_again_will_overwrite_those_changes", "<em>"+ctx.Tr("repo.commit_changes")+"</em>") | 					return | ||||||
| 						log.Error(4, "%s: %s / %s - %s", "EditFile", branchName, oldTreeName, "File updated by another user") |  | ||||||
| 						ctx.RenderWithErr(message, EDIT, &form) |  | ||||||
| 						return |  | ||||||
| 					} |  | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if oldTreeName != treeName { | 	if oldTreeName != treeName { | ||||||
| 		// We have a new filename (rename or completely new file) so we need to make sure it doesn't already exist, can't clobber | 		// We have a new filename (rename or completely new file) so we need to make sure it doesn't already exist, can't clobber. | ||||||
| 		_, err := ctx.Repo.Commit.GetTreeEntryByPath(treeName) | 		entry, err := ctx.Repo.Commit.GetTreeEntryByPath(treeName) | ||||||
| 		if err == nil { | 		if err != nil { | ||||||
|  | 			if !git.IsErrNotExist(err) { | ||||||
|  | 				ctx.Handle(500, "GetTreeEntryByPath", err) | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		if entry != nil { | ||||||
| 			ctx.Data["Err_Filename"] = true | 			ctx.Data["Err_Filename"] = true | ||||||
| 			ctx.RenderWithErr(ctx.Tr("repo.file_already_exists"), EDIT, &form) | 			ctx.RenderWithErr(ctx.Tr("repo.editor.file_already_exists", treeName), EDIT, &form) | ||||||
| 			log.Error(4, "%s: %s - %s", "NewFile", treeName, "File already exists, can't create new") |  | ||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	message := "" | 	var message string | ||||||
| 	if form.CommitSummary != "" { | 	if len(form.CommitSummary) > 0 { | ||||||
| 		message = strings.Trim(form.CommitSummary, " ") | 		message = strings.TrimSpace(form.CommitSummary) | ||||||
| 	} else { | 	} else { | ||||||
| 		if isNewFile { | 		if isNewFile { | ||||||
| 			message = ctx.Tr("repo.add") + " '" + treeName + "'" | 			message = ctx.Tr("repo.editor.add", treeName) | ||||||
| 		} else { | 		} else { | ||||||
| 			message = ctx.Tr("repo.update") + " '" + treeName + "'" | 			message = ctx.Tr("repo.editor.update", treeName) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	if strings.Trim(form.CommitMessage, " ") != "" { |  | ||||||
| 		message += "\n\n" + strings.Trim(form.CommitMessage, " ") | 	form.CommitMessage = strings.TrimSpace(form.CommitMessage) | ||||||
|  | 	if len(form.CommitMessage) > 0 { | ||||||
|  | 		message += "\n\n" + form.CommitMessage | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if err := ctx.Repo.Repository.UpdateRepoFile(ctx.User, oldBranchName, branchName, oldTreeName, treeName, content, message, isNewFile); err != nil { | 	if err := ctx.Repo.Repository.UpdateRepoFile(ctx.User, &models.UpdateRepoFileOptions{ | ||||||
|  | 		LastCommitID: lastCommit, | ||||||
|  | 		OldBranch:    oldBranchName, | ||||||
|  | 		NewBranch:    branchName, | ||||||
|  | 		OldTreeName:  oldTreeName, | ||||||
|  | 		NewTreeName:  treeName, | ||||||
|  | 		Message:      message, | ||||||
|  | 		Content:      content, | ||||||
|  | 		IsNewFile:    isNewFile, | ||||||
|  | 	}); err != nil { | ||||||
| 		ctx.Data["Err_Filename"] = true | 		ctx.Data["Err_Filename"] = true | ||||||
| 		ctx.RenderWithErr(ctx.Tr("repo.unable_to_update_file"), EDIT, &form) | 		ctx.RenderWithErr(ctx.Tr("repo.editor.failed_to_update_file", err), EDIT, &form) | ||||||
| 		log.Error(4, "%s: %v", "EditFile", err) |  | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if branch, err := ctx.Repo.Repository.GetBranch(branchName); err != nil { | 	ctx.Redirect(ctx.Repo.RepoLink + "/src/" + branchName + "/" + treeName) | ||||||
| 		log.Error(4, "repo.Repository.GetBranch(%s): %v", branchName, err) | } | ||||||
| 	} else if commit, err := branch.GetCommit(); err != nil { |  | ||||||
| 		log.Error(4, "branch.GetCommit(): %v", err) |  | ||||||
| 	} else { |  | ||||||
| 		pc := &models.PushCommits{ |  | ||||||
| 			Len:     1, |  | ||||||
| 			Commits: []*models.PushCommit{models.CommitToPushCommit(commit)}, |  | ||||||
| 		} |  | ||||||
| 		oldCommitID := ctx.Repo.CommitID |  | ||||||
| 		newCommitID := commit.ID.String() |  | ||||||
| 		if branchName != oldBranchName { |  | ||||||
| 			oldCommitID = "0000000000000000000000000000000000000000" // New Branch so we use all 0s |  | ||||||
| 		} |  | ||||||
| 		if err := models.CommitRepoAction(ctx.User.ID, ctx.Repo.Owner.ID, ctx.User.LowerName, ctx.Repo.Owner.Email, |  | ||||||
| 			ctx.Repo.Repository.ID, ctx.Repo.Owner.LowerName, ctx.Repo.Repository.Name, "refs/heads/"+branchName, pc, |  | ||||||
| 			oldCommitID, newCommitID); err != nil { |  | ||||||
| 			log.Error(4, "models.CommitRepoAction(branch = %s): %v", branchName, err) |  | ||||||
| 		} |  | ||||||
| 		models.HookQueue.Add(ctx.Repo.Repository.ID) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// Leaving this off until forked repos that get a branch can compare with forks master and not upstream | func EditFilePost(ctx *context.Context, form auth.EditRepoFileForm) { | ||||||
| 	//if oldBranchName != branchName { | 	editFilePost(ctx, form, false) | ||||||
| 	//	ctx.Redirect(EscapeUrl(ctx.Repo.RepoLink + "/compare/" + oldBranchName + "..." + branchName)) | } | ||||||
| 	//} else { |  | ||||||
| 	ctx.Redirect(EscapeUrl(ctx.Repo.RepoLink + "/src/" + branchName + "/" + treeName)) | func NewFilePost(ctx *context.Context, form auth.EditRepoFileForm) { | ||||||
| 	//} | 	editFilePost(ctx, form, true) | ||||||
| } | } | ||||||
|  |  | ||||||
| func DiffPreviewPost(ctx *context.Context, form auth.EditPreviewDiffForm) { | func DiffPreviewPost(ctx *context.Context, form auth.EditPreviewDiffForm) { | ||||||
| 	userName := ctx.Repo.Owner.Name |  | ||||||
| 	repoName := ctx.Repo.Repository.Name |  | ||||||
| 	branchName := ctx.Repo.BranchName |  | ||||||
| 	treeName := ctx.Repo.TreeName | 	treeName := ctx.Repo.TreeName | ||||||
| 	content := form.Content | 	content := form.Content | ||||||
|  |  | ||||||
| 	entry, err := ctx.Repo.Commit.GetTreeEntryByPath(treeName) | 	entry, err := ctx.Repo.Commit.GetTreeEntryByPath(treeName) | ||||||
| 	if (err != nil && git.IsErrNotExist(err)) || entry.IsDir() { | 	if err != nil { | ||||||
| 		ctx.Data["FileContent"] = content | 		if git.IsErrNotExist(err) { | ||||||
| 		ctx.HTML(200, DIFF_PREVIEW_NEW) | 			ctx.Data["FileContent"] = content | ||||||
|  | 			ctx.HTML(200, DIFF_PREVIEW_NEW) | ||||||
|  | 		} else { | ||||||
|  | 			ctx.Error(500, "GetTreeEntryByPath: "+err.Error()) | ||||||
|  | 		} | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	if entry.IsDir() { | ||||||
|  | 		ctx.Error(422) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	diff, err := ctx.Repo.Repository.GetPreviewDiff(models.RepoPath(userName, repoName), branchName, treeName, content, setting.Git.MaxGitDiffLines, setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles) | 	diff, err := ctx.Repo.Repository.GetDiffPreview(ctx.Repo.BranchName, treeName, content) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		ctx.Error(404, err.Error()) | 		ctx.Error(500, "GetDiffPreview: "+err.Error()) | ||||||
| 		log.Error(4, "%s: %v", "GetPreviewDiff", err) |  | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if diff.NumFiles() == 0 { | 	if diff.NumFiles() == 0 { | ||||||
| 		ctx.Error(200, ctx.Tr("repo.no_changes_to_show")) | 		ctx.PlainText(200, []byte(ctx.Tr("repo.editor.no_changes_to_show"))) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	ctx.Data["IsSplitStyle"] = ctx.Query("style") == "split" |  | ||||||
| 	ctx.Data["File"] = diff.Files[0] | 	ctx.Data["File"] = diff.Files[0] | ||||||
|  |  | ||||||
| 	ctx.HTML(200, DIFF_PREVIEW) | 	ctx.HTML(200, DIFF_PREVIEW) | ||||||
| } | } | ||||||
|  |  | ||||||
| func EscapeUrl(str string) string { |  | ||||||
| 	return strings.NewReplacer("?", "%3F", "%", "%25", "#", "%23", " ", "%20", "^", "%5E", "\\", "%5C", "{", "%7B", "}", "%7D", "|", "%7C").Replace(str) |  | ||||||
| } |  | ||||||
|   | |||||||
| @@ -170,12 +170,7 @@ func UploadFilePost(ctx *context.Context, form auth.UploadRepoFileForm) { | |||||||
| 		models.HookQueue.Add(ctx.Repo.Repository.ID) | 		models.HookQueue.Add(ctx.Repo.Repository.ID) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// Leaving this off until forked repos that get a branch can compare with forks master and not upstream | 	ctx.Redirect(ctx.Repo.RepoLink + "/src/" + branchName + "/" + treeName) | ||||||
| 	//if oldBranchName != branchName { |  | ||||||
| 	//	ctx.Redirect(EscapeUrl(ctx.Repo.RepoLink + "/compare/" + oldBranchName + "..." + branchName)) |  | ||||||
| 	//} else { |  | ||||||
| 	ctx.Redirect(EscapeUrl(ctx.Repo.RepoLink + "/src/" + branchName + "/" + treeName)) |  | ||||||
| 	//} |  | ||||||
| } | } | ||||||
|  |  | ||||||
| func UploadFileToServer(ctx *context.Context) { | func UploadFileToServer(ctx *context.Context) { | ||||||
|   | |||||||
| @@ -75,110 +75,112 @@ func Home(ctx *context.Context) { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	entry, err := ctx.Repo.Commit.GetTreeEntryByPath(treename) | 	entry, err := ctx.Repo.Commit.GetTreeEntryByPath(treename) | ||||||
| 	if err != nil && git.IsErrNotExist(err) { | 	if err != nil { | ||||||
| 		ctx.Handle(404, "GetTreeEntryByPath", err) | 		if git.IsErrNotExist(err) { | ||||||
|  | 			ctx.Handle(404, "GetTreeEntryByPath", err) | ||||||
|  | 		} else { | ||||||
|  | 			ctx.Handle(500, "GetTreeEntryByPath", err) | ||||||
|  | 		} | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if len(treename) != 0 && entry == nil { | 	if !entry.IsDir() { | ||||||
| 		ctx.Handle(404, "repo.Home", nil) |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	if entry != nil && !entry.IsDir() { |  | ||||||
| 		blob := entry.Blob() | 		blob := entry.Blob() | ||||||
|  | 		dataRc, err := blob.Data() | ||||||
| 		if dataRc, err := blob.Data(); err != nil { | 		if err != nil { | ||||||
| 			ctx.Handle(404, "blob.Data", err) | 			ctx.Handle(404, "blob.Data", err) | ||||||
| 			return | 			return | ||||||
| 		} else { | 		} | ||||||
| 			ctx.Data["FileSize"] = blob.Size() |  | ||||||
| 			ctx.Data["IsFile"] = true |  | ||||||
| 			ctx.Data["FileName"] = blob.Name() |  | ||||||
| 			ctx.Data["HighlightClass"] = highlight.FileNameToHighlightClass(blob.Name()) |  | ||||||
| 			ctx.Data["FileLink"] = rawLink + "/" + treename |  | ||||||
|  |  | ||||||
| 			buf := make([]byte, 1024) | 		ctx.Data["FileSize"] = blob.Size() | ||||||
| 			n, _ := dataRc.Read(buf) | 		ctx.Data["IsFile"] = true | ||||||
| 			if n > 0 { | 		ctx.Data["FileName"] = blob.Name() | ||||||
| 				buf = buf[:n] | 		ctx.Data["HighlightClass"] = highlight.FileNameToHighlightClass(blob.Name()) | ||||||
| 			} | 		ctx.Data["FileLink"] = rawLink + "/" + treename | ||||||
|  |  | ||||||
| 			_, isTextFile := base.IsTextFile(buf) | 		buf := make([]byte, 1024) | ||||||
| 			_, isImageFile := base.IsImageFile(buf) | 		n, _ := dataRc.Read(buf) | ||||||
| 			_, isPDFFile := base.IsPDFFile(buf) | 		if n > 0 { | ||||||
| 			ctx.Data["IsFileText"] = isTextFile | 			buf = buf[:n] | ||||||
|  | 		} | ||||||
|  |  | ||||||
| 			switch { | 		_, isTextFile := base.IsTextFile(buf) | ||||||
| 			case isPDFFile: | 		_, isImageFile := base.IsImageFile(buf) | ||||||
| 				ctx.Data["IsPDFFile"] = true | 		_, isPDFFile := base.IsPDFFile(buf) | ||||||
| 				ctx.Data["FileEditLinkTooltip"] = ctx.Tr("repo.cannot_edit_binary_files") | 		ctx.Data["IsFileText"] = isTextFile | ||||||
| 			case isImageFile: |  | ||||||
| 				ctx.Data["IsImageFile"] = true | 		switch { | ||||||
| 				ctx.Data["FileEditLinkTooltip"] = ctx.Tr("repo.cannot_edit_binary_files") | 		case isPDFFile: | ||||||
| 			case isTextFile: | 			ctx.Data["IsPDFFile"] = true | ||||||
| 				if blob.Size() >= setting.UI.MaxDisplayFileSize { | 			ctx.Data["FileEditLinkTooltip"] = ctx.Tr("repo.cannot_edit_binary_files") | ||||||
| 					ctx.Data["IsFileTooLarge"] = true | 		case isImageFile: | ||||||
|  | 			ctx.Data["IsImageFile"] = true | ||||||
|  | 			ctx.Data["FileEditLinkTooltip"] = ctx.Tr("repo.cannot_edit_binary_files") | ||||||
|  | 		case isTextFile: | ||||||
|  | 			if blob.Size() >= setting.UI.MaxDisplayFileSize { | ||||||
|  | 				ctx.Data["IsFileTooLarge"] = true | ||||||
|  | 			} else { | ||||||
|  | 				ctx.Data["IsFileTooLarge"] = false | ||||||
|  | 				d, _ := ioutil.ReadAll(dataRc) | ||||||
|  | 				buf = append(buf, d...) | ||||||
|  | 				readmeExist := markdown.IsMarkdownFile(blob.Name()) || markdown.IsReadmeFile(blob.Name()) | ||||||
|  | 				isMarkdown := readmeExist || markdown.IsMarkdownFile(blob.Name()) | ||||||
|  | 				ctx.Data["ReadmeExist"] = readmeExist | ||||||
|  | 				ctx.Data["IsMarkdown"] = isMarkdown | ||||||
|  | 				if isMarkdown { | ||||||
|  | 					ctx.Data["FileContent"] = string(markdown.Render(buf, path.Dir(treeLink), ctx.Repo.Repository.ComposeMetas())) | ||||||
| 				} else { | 				} else { | ||||||
| 					ctx.Data["IsFileTooLarge"] = false | 					// Building code view blocks with line number on server side. | ||||||
| 					d, _ := ioutil.ReadAll(dataRc) | 					var filecontent string | ||||||
| 					buf = append(buf, d...) | 					if err, content := template.ToUTF8WithErr(buf); err != nil { | ||||||
| 					readmeExist := markdown.IsMarkdownFile(blob.Name()) || markdown.IsReadmeFile(blob.Name()) | 						if err != nil { | ||||||
| 					isMarkdown := readmeExist || markdown.IsMarkdownFile(blob.Name()) | 							log.Error(4, "ToUTF8WithErr: %s", err) | ||||||
| 					ctx.Data["ReadmeExist"] = readmeExist | 						} | ||||||
| 					ctx.Data["IsMarkdown"] = isMarkdown | 						filecontent = string(buf) | ||||||
| 					if isMarkdown { |  | ||||||
| 						ctx.Data["FileContent"] = string(markdown.Render(buf, path.Dir(treeLink), ctx.Repo.Repository.ComposeMetas())) |  | ||||||
| 					} else { | 					} else { | ||||||
| 						// Building code view blocks with line number on server side. | 						filecontent = content | ||||||
| 						var filecontent string |  | ||||||
| 						if err, content := template.ToUTF8WithErr(buf); err != nil { |  | ||||||
| 							if err != nil { |  | ||||||
| 								log.Error(4, "ToUTF8WithErr: %s", err) |  | ||||||
| 							} |  | ||||||
| 							filecontent = string(buf) |  | ||||||
| 						} else { |  | ||||||
| 							filecontent = content |  | ||||||
| 						} |  | ||||||
|  |  | ||||||
| 						var output bytes.Buffer |  | ||||||
| 						lines := strings.Split(filecontent, "\n") |  | ||||||
| 						for index, line := range lines { |  | ||||||
| 							output.WriteString(fmt.Sprintf(`<li class="L%d" rel="L%d">%s</li>`, index+1, index+1, gotemplate.HTMLEscapeString(line)) + "\n") |  | ||||||
| 						} |  | ||||||
| 						ctx.Data["FileContent"] = gotemplate.HTML(output.String()) |  | ||||||
|  |  | ||||||
| 						output.Reset() |  | ||||||
| 						for i := 0; i < len(lines); i++ { |  | ||||||
| 							output.WriteString(fmt.Sprintf(`<span id="L%d">%d</span>`, i+1, i+1)) |  | ||||||
| 						} |  | ||||||
| 						ctx.Data["LineNums"] = gotemplate.HTML(output.String()) |  | ||||||
| 					} | 					} | ||||||
| 				} |  | ||||||
| 				if ctx.Repo.IsWriter() && ctx.Repo.IsViewBranch { | 					var output bytes.Buffer | ||||||
| 					ctx.Data["FileEditLink"] = editLink + "/" + treename | 					lines := strings.Split(filecontent, "\n") | ||||||
| 					ctx.Data["FileEditLinkTooltip"] = ctx.Tr("repo.edit_this_file") | 					for index, line := range lines { | ||||||
| 				} else { | 						output.WriteString(fmt.Sprintf(`<li class="L%d" rel="L%d">%s</li>`, index+1, index+1, gotemplate.HTMLEscapeString(line)) + "\n") | ||||||
| 					if !ctx.Repo.IsViewBranch { |  | ||||||
| 						ctx.Data["FileEditLinkTooltip"] = ctx.Tr("repo.must_be_on_branch") |  | ||||||
| 					} else if !ctx.Repo.IsWriter() { |  | ||||||
| 						ctx.Data["FileEditLink"] = forkLink |  | ||||||
| 						ctx.Data["FileEditLinkTooltip"] = ctx.Tr("repo.fork_before_edit") |  | ||||||
| 					} | 					} | ||||||
|  | 					ctx.Data["FileContent"] = gotemplate.HTML(output.String()) | ||||||
|  |  | ||||||
|  | 					output.Reset() | ||||||
|  | 					for i := 0; i < len(lines); i++ { | ||||||
|  | 						output.WriteString(fmt.Sprintf(`<span id="L%d">%d</span>`, i+1, i+1)) | ||||||
|  | 					} | ||||||
|  | 					ctx.Data["LineNums"] = gotemplate.HTML(output.String()) | ||||||
| 				} | 				} | ||||||
| 			default: |  | ||||||
| 				ctx.Data["FileEditLinkTooltip"] = ctx.Tr("repo.cannot_edit_binary_files") |  | ||||||
| 			} | 			} | ||||||
| 			if ctx.Repo.IsWriter() && ctx.Repo.IsViewBranch { | 			if ctx.Repo.IsWriter() && ctx.Repo.IsViewBranch { | ||||||
| 				ctx.Data["FileDeleteLink"] = deleteLink + "/" + treename | 				ctx.Data["FileEditLink"] = editLink + "/" + treename | ||||||
| 				ctx.Data["FileDeleteLinkTooltip"] = ctx.Tr("repo.delete_this_file") | 				ctx.Data["FileEditLinkTooltip"] = ctx.Tr("repo.edit_this_file") | ||||||
| 			} else { | 			} else { | ||||||
| 				if !ctx.Repo.IsViewBranch { | 				if !ctx.Repo.IsViewBranch { | ||||||
| 					ctx.Data["FileDeleteLinkTooltip"] = ctx.Tr("repo.must_be_on_branch") | 					ctx.Data["FileEditLinkTooltip"] = ctx.Tr("repo.must_be_on_branch") | ||||||
| 				} else if !ctx.Repo.IsWriter() { | 				} else if !ctx.Repo.IsWriter() { | ||||||
| 					ctx.Data["FileDeleteLinkTooltip"] = ctx.Tr("repo.must_be_writer") | 					ctx.Data["FileEditLink"] = forkLink | ||||||
|  | 					ctx.Data["FileEditLinkTooltip"] = ctx.Tr("repo.fork_before_edit") | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
|  | 		default: | ||||||
|  | 			ctx.Data["FileEditLinkTooltip"] = ctx.Tr("repo.cannot_edit_binary_files") | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | 		if ctx.Repo.IsWriter() && ctx.Repo.IsViewBranch { | ||||||
|  | 			ctx.Data["FileDeleteLink"] = deleteLink + "/" + treename | ||||||
|  | 			ctx.Data["FileDeleteLinkTooltip"] = ctx.Tr("repo.delete_this_file") | ||||||
|  | 		} else { | ||||||
|  | 			if !ctx.Repo.IsViewBranch { | ||||||
|  | 				ctx.Data["FileDeleteLinkTooltip"] = ctx.Tr("repo.must_be_on_branch") | ||||||
|  | 			} else if !ctx.Repo.IsWriter() { | ||||||
|  | 				ctx.Data["FileDeleteLinkTooltip"] = ctx.Tr("repo.must_be_writer") | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
| 	} else { | 	} else { | ||||||
| 		// Directory and file list. | 		// Directory and file list. | ||||||
| 		tree, err := ctx.Repo.Commit.SubTree(treename) | 		tree, err := ctx.Repo.Commit.SubTree(treename) | ||||||
|   | |||||||
| @@ -71,9 +71,9 @@ | |||||||
| 					{{if not $file.IsSubmodule}} | 					{{if not $file.IsSubmodule}} | ||||||
| 						<div class="ui right"> | 						<div class="ui right"> | ||||||
| 							{{if $file.IsDeleted}} | 							{{if $file.IsDeleted}} | ||||||
| 								<a class="ui basic tiny button" rel="nofollow" href="{{EscapePound $.BeforeSourcePath}}/{{EscapePound .Name}}">{{$.i18n.Tr "repo.diff.view_file"}}</a> | 								<a class="ui basic grey tiny button" rel="nofollow" href="{{EscapePound $.BeforeSourcePath}}/{{EscapePound .Name}}">{{$.i18n.Tr "repo.diff.view_file"}}</a> | ||||||
| 							{{else}} | 							{{else}} | ||||||
| 								<a class="ui basic tiny button" rel="nofollow" href="{{EscapePound $.SourcePath}}/{{EscapePound .Name}}">{{$.i18n.Tr "repo.diff.view_file"}}</a> | 								<a class="ui basic grey tiny button" rel="nofollow" href="{{EscapePound $.SourcePath}}/{{EscapePound .Name}}">{{$.i18n.Tr "repo.diff.view_file"}}</a> | ||||||
| 							{{end}} | 							{{end}} | ||||||
| 						</div> | 						</div> | ||||||
| 					{{end}} | 					{{end}} | ||||||
|   | |||||||
| @@ -2,11 +2,10 @@ | |||||||
| <div class="repository file edit"> | <div class="repository file edit"> | ||||||
| 	{{template "repo/header" .}} | 	{{template "repo/header" .}} | ||||||
| 	<div class="ui container"> | 	<div class="ui container"> | ||||||
| 		{{.branchName}} |  | ||||||
| 		{{template "base/alert" .}} | 		{{template "base/alert" .}} | ||||||
| 		<form class="ui edit form" action="{{EscapePound $.Link}}" method="post"> | 		<form class="ui edit form" action="{{EscapePound $.Link}}" method="post"> | ||||||
| 			{{.CsrfTokenHtml}} | 			{{.CsrfTokenHtml}} | ||||||
| 			<input type="hidden" name="last_commit" value="{{.LastCommit}}"> | 			<input type="hidden" name="last_commit" value="{{.last_commit}}"> | ||||||
| 			<div class="ui secondary menu"> | 			<div class="ui secondary menu"> | ||||||
| 				<div class="item fitted" style="width:100%;"> | 				<div class="item fitted" style="width:100%;"> | ||||||
| 					<div class="ui breadcrumb field{{if .Err_Filename}} error{{end}}"> | 					<div class="ui breadcrumb field{{if .Err_Filename}} error{{end}}"> | ||||||
| @@ -31,7 +30,7 @@ | |||||||
| 				<div class="ui top attached tabular menu" data-write="write" data-preview="preview" data-diff="diff"> | 				<div class="ui top attached tabular menu" data-write="write" data-preview="preview" data-diff="diff"> | ||||||
| 					<a class="active item" data-tab="write"><i class="octicon octicon-code"></i> {{.i18n.Tr "repo.edit_file"}}</a> | 					<a class="active item" data-tab="write"><i class="octicon octicon-code"></i> {{.i18n.Tr "repo.edit_file"}}</a> | ||||||
| 					<a class="item" data-tab="preview" data-url="{{AppSubUrl}}/api/v1/markdown" data-context="{{.RepoLink}}" data-preview-file-modes="{{.PreviewableFileModes}}"><i class="octicon octicon-eye"></i> {{.i18n.Tr "repo.release.preview"}}</a> | 					<a class="item" data-tab="preview" data-url="{{AppSubUrl}}/api/v1/markdown" data-context="{{.RepoLink}}" data-preview-file-modes="{{.PreviewableFileModes}}"><i class="octicon octicon-eye"></i> {{.i18n.Tr "repo.release.preview"}}</a> | ||||||
| 					<a class="item" data-tab="diff" data-url="{{.PreviewDiffURL}}" data-context="{{.BranchLink}}"><i class="octicon octicon-diff"></i> {{.i18n.Tr "repo.preview_changes"}}</a> | 					<a class="item" data-tab="diff" data-url="{{.RepoLink}}/preview/{{.BranchName}}/{{.TreeName}}" data-context="{{.BranchLink}}"><i class="octicon octicon-diff"></i> {{.i18n.Tr "repo.preview_changes"}}</a> | ||||||
| 				</div> | 				</div> | ||||||
| 				<div class="ui bottom attached active tab segment" data-tab="write"> | 				<div class="ui bottom attached active tab segment" data-tab="write"> | ||||||
| 					<textarea id="edit_area" name="content" data-id="repo-{{.Repository.Name}}-{{.TreeName}}" | 					<textarea id="edit_area" name="content" data-id="repo-{{.Repository.Name}}-{{.TreeName}}" | ||||||
| @@ -53,26 +52,26 @@ | |||||||
| 				<div class="commit-form"> | 				<div class="commit-form"> | ||||||
| 					<h3>{{.i18n.Tr "repo.commit_changes"}}</h3> | 					<h3>{{.i18n.Tr "repo.commit_changes"}}</h3> | ||||||
| 					<div class="field"> | 					<div class="field"> | ||||||
| 						<input name="commit_summary" placeholder="{{if .IsNewFile}}{{.i18n.Tr "repo.add"}} '{{.TreeName}}/<filename>'{{else}}{{.i18n.Tr "repo.update"}} '{{.TreeName}}'{{end}}" value="{{.CommitSummary}}"> | 						<input name="commit_summary" placeholder="{{if .IsNewFile}}{{.i18n.Tr "repo.add"}} '{{.TreeName}}/<filename>'{{else}}{{.i18n.Tr "repo.update"}} '{{.TreeName}}'{{end}}" value="{{.commit_summary}}"> | ||||||
| 					</div> | 					</div> | ||||||
| 					<div class="field"> | 					<div class="field"> | ||||||
| 						<textarea name="commit_message" placeholder="{{.i18n.Tr "repo.default_commit_message"}}">{{.CommitMessage}}</textarea> | 						<textarea name="commit_message" placeholder="{{.i18n.Tr "repo.default_commit_message"}}">{{.commit_message}}</textarea> | ||||||
| 					</div> | 					</div> | ||||||
| 					<div class="quick-pull-choice js-quick-pull-choice "> | 					<div class="quick-pull-choice js-quick-pull-choice"> | ||||||
| 						<dl class="form-group"> | 						<dl class="form-group"> | ||||||
| 							<dd> | 							<dd> | ||||||
| 						 		<div class="form-checkbox"> | 						 		<div class="form-checkbox"> | ||||||
| 									<label> | 									<label> | ||||||
| 										<input type="radio" class="js-quick-pull-choice-option" name="commit_choice" value="direct"{{if eq .CommitChoice "direct"}} checked="checked"{{end}}> | 										<input type="radio" class="js-quick-pull-choice-option" name="commit_choice" value="direct"{{if eq .commit_choice "direct"}} checked="checked"{{end}}> | ||||||
| 										<i class="octicon octicon-git-commit" height="16" width="14"></i> | 										<i class="octicon octicon-git-commit" height="16" width="14"></i> | ||||||
| 										{{.CommitDirectlyToThisBranch | Safe}} | 										{{.i18n.Tr "repo.editor.commit_directly_to_this_branch" .BranchName | Safe}} | ||||||
| 									</label> | 									</label> | ||||||
| 								</div> | 								</div> | ||||||
| 								<div class="form-checkbox"> | 								<div class="form-checkbox"> | ||||||
| 									<label> | 									<label> | ||||||
| 										<input type="radio" class="js-quick-pull-choice-option" name="commit_choice" value="commit-to-new-branch"{{if eq .CommitChoice "commit-to-new-branch"}} checked="checked"{{end}}> | 										<input type="radio" class="js-quick-pull-choice-option" name="commit_choice" value="commit-to-new-branch"{{if eq .commit_choice "commit-to-new-branch"}} checked="checked"{{end}}> | ||||||
| 										<i class="octicon octicon-git-pull-request" height="16" width="12"></i> | 										<i class="octicon octicon-git-pull-request" height="16" width="12"></i> | ||||||
| 										{{.CreateNewBranch | Safe}} | 										{{.i18n.Tr "repo.editor.create_new_branch" | Safe}} | ||||||
| 									</label> | 									</label> | ||||||
| 								</div> | 								</div> | ||||||
| 							</dd> | 							</dd> | ||||||
| @@ -80,7 +79,7 @@ | |||||||
| 						<div class="quick-pull-branch-name"> | 						<div class="quick-pull-branch-name"> | ||||||
| 							<div class="new-branch-name-input{{if .Err_Branchname}} error{{end}}"> | 							<div class="new-branch-name-input{{if .Err_Branchname}} error{{end}}"> | ||||||
| 								<i class="octicon octicon-git-branch quick-pull-new-branch-icon" height="16" width="10"></i> | 								<i class="octicon octicon-git-branch quick-pull-new-branch-icon" height="16" width="10"></i> | ||||||
| 								<input type="text" name="new_branch_name" value="{{.NewBranchName}}" class="form-control input-contrast mr-2 js-quick-pull-new-branch-name" placeholder="New branch name…"> | 								<input type="text" name="new_branch_name" value="{{.new_branch_name}}" class="form-control input-contrast mr-2 js-quick-pull-new-branch-name" placeholder="New branch name…"> | ||||||
| 								<span class="text-muted js-quick-pull-normalization-info"></span> | 								<span class="text-muted js-quick-pull-normalization-info"></span> | ||||||
| 							</div> | 							</div> | ||||||
| 						</div> | 						</div> | ||||||
		Reference in New Issue
	
	Block a user