Class: Overcommit::HookContext::PreCommit

Inherits:
Base
  • Object
show all
Defined in:
lib/overcommit/hook_context/pre_commit.rb

Overview

Contains helpers related to contextual information used by pre-commit hooks.

This includes staged files, which lines of those files have been modified, etc. It is also responsible for saving/restoring the state of the repo so hooks only inspect staged changes.

Instance Method Summary collapse

Methods inherited from Base

#all_files, #execute_hook, #hook_class_name, #hook_script_name, #hook_type_name, #initialize, #input_lines, #input_string, #post_fail_message

Constructor Details

This class inherits a constructor from Overcommit::HookContext::Base

Instance Method Details

#amendment?Boolean

Returns whether this hook run was triggered by `git commit –amend`


14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/overcommit/hook_context/pre_commit.rb', line 14

def amendment?
  return @amendment unless @amendment.nil?

  cmd = Overcommit::Utils.parent_command
  return unless cmd
  amend_pattern = 'commit(\s.*)?\s--amend(\s|$)'

  # Since the ps command can return invalid byte sequences for commands
  # containing unicode characters, we replace the offending characters,
  # since the pattern we're looking for will consist of ASCII characters
  unless cmd.valid_encoding?
    cmd = Overcommit::Utils.parent_command.encode('UTF-16be', invalid: :replace, replace: '?').
                                           encode('UTF-8')
  end

  return @amendment if
    # True if the command is a commit with the --amend flag
    @amendment = !(/\s#{amend_pattern}/ =~ cmd).nil?

  # Check for git aliases that call `commit --amend`
  `git config --get-regexp "^alias\\." "#{amend_pattern}"`.
    scan(/alias\.([-\w]+)/). # Extract the alias
    each do |match|
      return @amendment if
        # True if the command uses a git alias for `commit --amend`
        @amendment = !(/git(\.exe)?\s+#{match[0]}/ =~ cmd).nil?
    end

  @amendment
end

#cleanup_environmentObject

Restore unstaged changes and reset file modification times so it appears as if nothing ever changed.

We want to restore the modification times for each of the files after every step to ensure as little time as possible has passed while the modification time on the file was newer. This helps us play more nicely with file watchers.


70
71
72
73
74
75
76
77
78
79
# File 'lib/overcommit/hook_context/pre_commit.rb', line 70

def cleanup_environment
  if @changes_stashed
    clear_working_tree
    restore_working_tree
    restore_modified_times
  end

  Overcommit::GitRepo.restore_merge_state
  Overcommit::GitRepo.restore_cherry_pick_state
end

#initial_commit?Boolean

Returns whether the current git branch is empty (has no commits).


117
118
119
120
# File 'lib/overcommit/hook_context/pre_commit.rb', line 117

def initial_commit?
  return @initial_commit unless @initial_commit.nil?
  @initial_commit = Overcommit::GitRepo.initial_commit?
end

#modified_filesObject

Get a list of added, copied, or modified files that have been staged. Renames and deletions are ignored, since there should be nothing to check.


83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/overcommit/hook_context/pre_commit.rb', line 83

def modified_files
  unless @modified_files
    currently_staged = Overcommit::GitRepo.modified_files(staged: true)
    @modified_files = currently_staged

    # Include files modified in last commit if amending
    if amendment?
      subcmd = 'show --format=%n'
      previously_modified = Overcommit::GitRepo.modified_files(subcmd: subcmd)
      @modified_files |= filter_modified_files(previously_modified)
    end
  end
  @modified_files
end

#modified_lines_in_file(file) ⇒ Object

Returns the set of line numbers corresponding to the lines that were changed in a specified file.


100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/overcommit/hook_context/pre_commit.rb', line 100

def modified_lines_in_file(file)
  @modified_lines ||= {}
  unless @modified_lines[file]
    @modified_lines[file] =
      Overcommit::GitRepo.extract_modified_lines(file, staged: true)

    # Include lines modified in last commit if amending
    if amendment?
      subcmd = 'show --format=%n'
      @modified_lines[file] +=
        Overcommit::GitRepo.extract_modified_lines(file, subcmd: subcmd)
    end
  end
  @modified_lines[file]
end

#setup_environmentObject

Stash unstaged contents of files so hooks don't see changes that aren't about to be committed.


47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/overcommit/hook_context/pre_commit.rb', line 47

def setup_environment
  store_modified_times
  Overcommit::GitRepo.store_merge_state
  Overcommit::GitRepo.store_cherry_pick_state

  # Don't attempt to stash changes if all changes are staged, as this
  # prevents us from modifying files at all, which plays better with
  # editors/tools which watch for file changes.
  if !initial_commit? && unstaged_changes?
    stash_changes

    # While running hooks make it appear as if nothing changed
    restore_modified_times
  end
end