Module: Overcommit::GitRepo
- Defined in:
- lib/overcommit/git_repo.rb
Overview
Provide a set of utilities for certain interactions with ‘git`.
Defined Under Namespace
Classes: Submodule, SubmoduleStatus
Constant Summary collapse
- DIFF_HUNK_REGEX =
Regular expression used to extract diff ranges from hunks of diff output.
/ ^@@\s [^\s]+\s # Ignore old file range \+(\d+)(?:,(\d+))? # Extract range of hunk containing start line and number of lines \s@@.*$ /x.freeze
- SUBMODULE_STATUS_REGEX =
Regular expression used to extract information from lines of ‘git submodule status` output
/ ^\s*(?<prefix>[-+U]?)(?<sha1>\w+) \s(?<path>[^\s]+?) (?:\s\((?<describe>.+)\))?$ /x.freeze
Class Method Summary collapse
-
.all_files ⇒ Array<String>
Returns the names of all files that are tracked by git.
-
.branches_containing_commit(commit_ref) ⇒ Array<String>
Returns the names of all branches containing the given commit.
-
.current_branch ⇒ String
Returns the name of the currently checked out branch.
-
.extract_modified_lines(file_path, options) ⇒ Set
Extract the set of modified lines from a given file.
-
.initial_commit? ⇒ true, false
Returns whether the current git branch is empty (has no commits).
-
.list_files(paths = [], options = {}) ⇒ Array<String>
Returns the names of files in the given paths that are tracked by git.
-
.modified_files(options) ⇒ Array<String>
Returns the names of all files that have been modified compared to HEAD.
-
.restore_cherry_pick_state ⇒ Object
Restore any relevant files that were present when repo was in the middle of a cherry-pick.
-
.restore_merge_state ⇒ Object
Restore any relevant files that were present when repo was in the middle of a merge.
-
.staged_submodule_removals ⇒ Object
Returns the submodules that have been staged for removal.
-
.store_cherry_pick_state ⇒ Object
Store any relevant files that are present when repo is in the middle of a cherry-pick.
-
.store_merge_state ⇒ Object
Store any relevant files that are present when repo is in the middle of a merge.
-
.submodule_statuses(options = {}) ⇒ Array<SubmoduleStatus>
Returns a list of SubmoduleStatus objects, one for each submodule in the parent repository.
-
.submodules(options = {}) ⇒ Array<Overcommit::GitRepo::Submodule>
Returns the current set of registered submodules.
-
.tracked?(path) ⇒ true, false
Returns whether the specified file/path is tracked by this repository.
Class Method Details
.all_files ⇒ Array<String>
Returns the names of all files that are tracked by git.
137 138 139 140 141 142 |
# File 'lib/overcommit/git_repo.rb', line 137 def all_files `git ls-files`. split(/\n/). map { |relative_file| File.(relative_file) }. reject { |file| File.directory?(file) } # Exclude submodule directories end |
.branches_containing_commit(commit_ref) ⇒ Array<String>
Returns the names of all branches containing the given commit.
274 275 276 277 278 279 |
# File 'lib/overcommit/git_repo.rb', line 274 def branches_containing_commit(commit_ref) `git branch --column=dense --contains #{commit_ref}`. sub(/\((HEAD )?detached (from|at) .*?\)/, ''). # ignore detached HEAD split(/\s+/). reject { |s| s.empty? || s == '*' } end |
.current_branch ⇒ String
Returns the name of the currently checked out branch.
283 284 285 |
# File 'lib/overcommit/git_repo.rb', line 283 def current_branch `git symbolic-ref --short -q HEAD`.chomp end |
.extract_modified_lines(file_path, options) ⇒ Set
Extract the set of modified lines from a given file.
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
# File 'lib/overcommit/git_repo.rb', line 68 def extract_modified_lines(file_path, ) lines = Set.new flags = '--cached' if [:staged] refs = [:refs] subcmd = [:subcmd] || 'diff' `git #{subcmd} --no-color --no-ext-diff -U0 #{flags} #{refs} -- "#{file_path}"`. scan(DIFF_HUNK_REGEX) do |start_line, lines_added| lines_added = (lines_added || 1).to_i # When blank, one line was added cur_line = start_line.to_i lines_added.times do lines.add cur_line cur_line += 1 end end lines end |
.initial_commit? ⇒ true, false
Returns whether the current git branch is empty (has no commits).
146 147 148 |
# File 'lib/overcommit/git_repo.rb', line 146 def initial_commit? !Overcommit::Utils.execute(%w[git rev-parse HEAD]).success? end |
.list_files(paths = [], options = {}) ⇒ Array<String>
Returns the names of files in the given paths that are tracked by git.
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 |
# File 'lib/overcommit/git_repo.rb', line 110 def list_files(paths = [], = {}) ref = [:ref] || 'HEAD' result = Overcommit::Utils.execute(%W[git ls-tree --name-only #{ref}], args: paths) unless result.success? raise Overcommit::Exceptions::Error, "Error listing files. EXIT STATUS(es): #{result.statuses}.\n" \ "STDOUT(s): #{result.stdouts}.\n" \ "STDERR(s): #{result.stderrs}." end result.stdout.split(/\n/). map { |relative_file| File.(relative_file) }. reject { |file| File.directory?(file) } # Exclude submodule directories end |
.modified_files(options) ⇒ Array<String>
Returns the names of all files that have been modified compared to HEAD.
93 94 95 96 97 98 99 100 101 102 103 |
# File 'lib/overcommit/git_repo.rb', line 93 def modified_files() flags = '--cached' if [:staged] refs = [:refs] subcmd = [:subcmd] || 'diff' `git #{subcmd} --name-only -z --diff-filter=ACMR --ignore-submodules=all #{flags} #{refs}`. split("\0"). map(&:strip). reject(&:empty?). map { |relative_file| File.(relative_file) } end |
.restore_cherry_pick_state ⇒ Object
Restore any relevant files that were present when repo was in the middle of a cherry-pick.
203 204 205 206 207 208 209 210 211 |
# File 'lib/overcommit/git_repo.rb', line 203 def restore_cherry_pick_state if @cherry_head File.open(File.('CHERRY_PICK_HEAD', Overcommit::Utils.git_dir), 'w') do |f| f.write(@cherry_head) end @cherry_head = nil end end |
.restore_merge_state ⇒ Object
Restore any relevant files that were present when repo was in the middle of a merge.
183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 |
# File 'lib/overcommit/git_repo.rb', line 183 def restore_merge_state if @merge_head FileUtils.touch(File.('MERGE_MODE', Overcommit::Utils.git_dir)) File.open(File.('MERGE_HEAD', Overcommit::Utils.git_dir), 'w') do |f| f.write(@merge_head) end @merge_head = nil end if @merge_msg File.open(File.('MERGE_MSG', Overcommit::Utils.git_dir), 'w') do |f| f.write("#{@merge_msg}\n") end @merge_msg = nil end end |
.staged_submodule_removals ⇒ Object
Returns the submodules that have been staged for removal.
‘git` has an unexpected behavior where removing a submodule without committing (i.e. such that the submodule directory is removed and the changes to the index are staged) and then doing a hard reset results in the index being wiped but the empty directory of the once existent submodule being restored (but with no content).
This prevents restoration of the stash of the submodule index changes, which breaks pre-commit hook restorations of the working index.
Thus we expose this helper so the restoration code can manually delete the directory.
231 232 233 234 235 236 237 238 239 |
# File 'lib/overcommit/git_repo.rb', line 231 def staged_submodule_removals # There were no submodules before, so none could have been removed return [] if `git ls-files .gitmodules`.empty? previous = submodules(ref: 'HEAD') current = submodules previous - current end |
.store_cherry_pick_state ⇒ Object
Store any relevant files that are present when repo is in the middle of a cherry-pick.
Restored via [#restore_cherry_pick_state].
171 172 173 174 175 176 177 178 179 |
# File 'lib/overcommit/git_repo.rb', line 171 def store_cherry_pick_state cherry_head = `git rev-parse CHERRY_PICK_HEAD 2> #{File::NULL}`.chomp # Store the merge state if we're in the middle of resolving a merge # conflict. This is necessary since stashing removes the merge state. if cherry_head != 'CHERRY_PICK_HEAD' @cherry_head = cherry_head end end |
.store_merge_state ⇒ Object
Store any relevant files that are present when repo is in the middle of a merge.
Restored via [#restore_merge_state].
154 155 156 157 158 159 160 161 162 163 164 165 |
# File 'lib/overcommit/git_repo.rb', line 154 def store_merge_state merge_head = `git rev-parse MERGE_HEAD 2> #{File::NULL}`.chomp # Store the merge state if we're in the middle of resolving a merge # conflict. This is necessary since stashing removes the merge state. if merge_head != 'MERGE_HEAD' @merge_head = merge_head end merge_msg_file = File.('MERGE_MSG', Overcommit::Utils.git_dir) @merge_msg = File.open(merge_msg_file).read if File.exist?(merge_msg_file) end |
.submodule_statuses(options = {}) ⇒ Array<SubmoduleStatus>
Returns a list of SubmoduleStatus objects, one for each submodule in the parent repository.
53 54 55 56 57 58 59 60 61 |
# File 'lib/overcommit/git_repo.rb', line 53 def submodule_statuses( = {}) flags = '--recursive' if [:recursive] `git submodule status #{flags}`. scan(SUBMODULE_STATUS_REGEX). map do |prefix, sha1, path, describe| SubmoduleStatus.new(prefix, sha1, path, describe) end end |
.submodules(options = {}) ⇒ Array<Overcommit::GitRepo::Submodule>
Returns the current set of registered submodules.
245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 |
# File 'lib/overcommit/git_repo.rb', line 245 def submodules( = {}) ref = [:ref] modules = [] IniParse.parse(`git show #{ref}:.gitmodules 2> #{File::NULL}`).each do |section| # git < 1.8.5 does not update the .gitmodules file with submodule # changes, so when we are looking at the current state of the work tree, # we need to check if the submodule actually exists via another method, # since the .gitmodules file we parsed does not represent reality. if ref.nil? && GIT_VERSION < '1.8.5' result = Overcommit::Utils.execute(%W[ git submodule status #{section['path']} ]) next unless result.success? end modules << Submodule.new(section['path'], section['url']) end modules rescue IniParse::IniParseError => e raise Overcommit::Exceptions::GitSubmoduleError, "Unable to read submodule information from #{ref}:.gitmodules file: #{e.}" end |
.tracked?(path) ⇒ true, false
Returns whether the specified file/path is tracked by this repository.
130 131 132 |
# File 'lib/overcommit/git_repo.rb', line 130 def tracked?(path) Overcommit::Utils.execute(%W[git ls-files #{path} --error-unmatch]).success? end |