summaryrefslogtreecommitdiff
path: root/lib/gitlab/git/keep_around.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/gitlab/git/keep_around.rb')
-rw-r--r--lib/gitlab/git/keep_around.rb48
1 files changed, 48 insertions, 0 deletions
diff --git a/lib/gitlab/git/keep_around.rb b/lib/gitlab/git/keep_around.rb
new file mode 100644
index 00000000000..d58f10bdbb7
--- /dev/null
+++ b/lib/gitlab/git/keep_around.rb
@@ -0,0 +1,48 @@
+# frozen_string_literal: true
+
+# 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
+# example if they have comments or CI builds.
+#
+# For Geo's sake, pass in multiple shas rather than calling it multiple times,
+# to avoid unnecessary syncing.
+module Gitlab
+ module Git
+ class KeepAround
+ def self.execute(repository, shas)
+ new(repository).execute(shas)
+ end
+
+ def initialize(repository)
+ @repository = repository
+ end
+
+ def execute(shas)
+ shas.each do |sha|
+ next unless sha.present? && commit_by(oid: sha)
+
+ next if kept_around?(sha)
+
+ # This will still fail if the file is corrupted (e.g. 0 bytes)
+ raw_repository.write_ref(keep_around_ref_name(sha), sha)
+ rescue Gitlab::Git::CommandError => ex
+ Rails.logger.error "Unable to create keep-around reference for repository #{disk_path}: #{ex}" # rubocop:disable Gitlab/RailsLogger
+ end
+ end
+
+ def kept_around?(sha)
+ ref_exists?(keep_around_ref_name(sha))
+ end
+
+ delegate :commit_by, :raw_repository, :ref_exists?, :disk_path, to: :@repository
+ private :commit_by, :raw_repository, :ref_exists?, :disk_path
+
+ private
+
+ def keep_around_ref_name(sha)
+ "refs/#{::Repository::REF_KEEP_AROUND}/#{sha}"
+ end
+ end
+ end
+end