diff options
author | Jacob Vosmaer <jacob@gitlab.com> | 2016-08-30 16:06:40 +0200 |
---|---|---|
committer | Jacob Vosmaer <jacob@gitlab.com> | 2016-09-02 11:49:40 +0200 |
commit | a93a610bac7d9ee7c0908592b6a5d91ef0d94333 (patch) | |
tree | eeec3f3161f74abd2f213145c7ce9d55f5f471e1 /app | |
parent | fd1741b47970fc52d994367ba38b5d1353d94725 (diff) | |
download | gitlab-ce-a93a610bac7d9ee7c0908592b6a5d91ef0d94333.tar.gz |
Use 'git update-ref' for safer web commits
Diffstat (limited to 'app')
-rw-r--r-- | app/models/repository.rb | 41 |
1 files changed, 21 insertions, 20 deletions
diff --git a/app/models/repository.rb b/app/models/repository.rb index f891e8374d2..b0644259af8 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -149,7 +149,7 @@ class Repository return false unless target GitHooksService.new.execute(user, path_to_repo, oldrev, target, ref) do - rugged.branches.create(branch_name, target) + update_ref!(ref, target, oldrev) end after_create_branch @@ -181,7 +181,7 @@ class Repository ref = Gitlab::Git::BRANCH_REF_PREFIX + branch_name GitHooksService.new.execute(user, path_to_repo, oldrev, newrev, ref) do - rugged.branches.delete(branch_name) + update_ref!(ref, newrev, oldrev) end after_remove_branch @@ -215,6 +215,21 @@ class Repository rugged.references.exist?(ref) end + def update_ref!(name, newrev, oldrev) + # We use 'git update-ref' because libgit2/rugged currently does not + # offer 'compare and swap' ref updates. Without compare-and-swap we can + # (and have!) accidentally reset the ref to an earlier state, clobbering + # commits. See also https://github.com/libgit2/libgit2/issues/1534. + command = %w[git update-ref --stdin -z] + output, status = Gitlab::Popen.popen(command, path_to_repo) do |stdin| + stdin.write("update #{name}\x00#{newrev}\x00#{oldrev}\x00") + end + + return if status.zero? + + raise CommitError.new("error updating ref #{name} #{oldrev}->#{newrev}\n#{output}") + end + # Makes sure a commit is kept around when Git garbage collection runs. # Git GC will delete commits from the repository that are no longer in any # branches or tags, but we want to keep some of these commits around, for @@ -1014,15 +1029,10 @@ class Repository def commit_with_hooks(current_user, branch) update_autocrlf_option - oldrev = Gitlab::Git::BLANK_SHA ref = Gitlab::Git::BRANCH_REF_PREFIX + branch target_branch = find_branch(branch) was_empty = empty? - if !was_empty && target_branch - oldrev = target_branch.target.id - end - # Make commit newrev = yield(ref) @@ -1030,24 +1040,15 @@ class Repository raise CommitError.new('Failed to create commit') end + oldrev = rugged.lookup(newrev).parent_ids.first || Gitlab::Git::BLANK_SHA + GitHooksService.new.execute(current_user, path_to_repo, oldrev, newrev, ref) do + update_ref!(ref, newrev, oldrev) + if was_empty || !target_branch - # Create branch - rugged.references.create(ref, newrev) - # If repo was empty expire cache after_create if was_empty after_create_branch - else - # Update head - current_head = find_branch(branch).target.id - - # Make sure target branch was not changed during pre-receive hook - if current_head == oldrev - rugged.references.update(ref, newrev) - else - raise CommitError.new('Commit was rejected because branch received new push') - end end end |