UpdateRepoFile adds or updates a file in repository.
(doer *User, opts UpdateRepoFileOptions)
| 141 | |
| 142 | // UpdateRepoFile adds or updates a file in repository. |
| 143 | func (r *Repository) UpdateRepoFile(doer *User, opts UpdateRepoFileOptions) error { |
| 144 | // 🚨 SECURITY: Prevent uploading files into the ".git" directory. |
| 145 | if isRepositoryGitPath(opts.NewTreeName) { |
| 146 | return errors.Errorf("bad tree path %q", opts.NewTreeName) |
| 147 | } |
| 148 | |
| 149 | repoWorkingPool.CheckIn(com.ToStr(r.ID)) |
| 150 | defer repoWorkingPool.CheckOut(com.ToStr(r.ID)) |
| 151 | |
| 152 | if err := r.DiscardLocalRepoBranchChanges(opts.OldBranch); err != nil { |
| 153 | return errors.Newf("discard local repo branch[%s] changes: %v", opts.OldBranch, err) |
| 154 | } else if err = r.UpdateLocalCopyBranch(opts.OldBranch); err != nil { |
| 155 | return errors.Newf("update local copy branch[%s]: %v", opts.OldBranch, err) |
| 156 | } |
| 157 | |
| 158 | localPath := r.LocalCopyPath() |
| 159 | |
| 160 | // 🚨 SECURITY: Prevent touching files in surprising places, reject operations |
| 161 | // that involve symlinks. |
| 162 | if hasSymlinkInPath(localPath, opts.OldTreeName) || hasSymlinkInPath(localPath, opts.NewTreeName) { |
| 163 | return errors.New("cannot update file with symbolic link in path") |
| 164 | } |
| 165 | |
| 166 | repoPath := r.RepoPath() |
| 167 | if opts.OldBranch != opts.NewBranch { |
| 168 | // Directly return error if new branch already exists in the server |
| 169 | if git.RepoHasBranch(repoPath, opts.NewBranch) { |
| 170 | return BranchAlreadyExists{Name: opts.NewBranch} |
| 171 | } |
| 172 | |
| 173 | // Otherwise, delete branch from local copy in case out of sync |
| 174 | if git.RepoHasBranch(localPath, opts.NewBranch) { |
| 175 | if err := git.DeleteBranch(localPath, opts.NewBranch, git.DeleteBranchOptions{ |
| 176 | Force: true, |
| 177 | }); err != nil { |
| 178 | return errors.Newf("delete branch %q: %v", opts.NewBranch, err) |
| 179 | } |
| 180 | } |
| 181 | |
| 182 | if err := r.CheckoutNewBranch(opts.OldBranch, opts.NewBranch); err != nil { |
| 183 | return errors.Newf("checkout new branch[%s] from old branch[%s]: %v", opts.NewBranch, opts.OldBranch, err) |
| 184 | } |
| 185 | } |
| 186 | |
| 187 | oldFilePath := path.Join(localPath, opts.OldTreeName) |
| 188 | newFilePath := path.Join(localPath, opts.NewTreeName) |
| 189 | |
| 190 | // Prompt the user if the meant-to-be new file already exists. |
| 191 | if osutil.Exist(newFilePath) && opts.IsNewFile { |
| 192 | return ErrRepoFileAlreadyExist{newFilePath} |
| 193 | } |
| 194 | |
| 195 | if err := os.MkdirAll(path.Dir(newFilePath), os.ModePerm); err != nil { |
| 196 | return errors.Wrapf(err, "create parent directories of %q", newFilePath) |
| 197 | } |
| 198 | |
| 199 | if osutil.IsFile(oldFilePath) && opts.OldTreeName != opts.NewTreeName { |
| 200 | if err := git.Move(localPath, opts.OldTreeName, opts.NewTreeName); err != nil { |
no test coverage detected