Class: GitCommit

Inherits:
Object
  • Object
show all
Defined in:
lib/commit/git_commit.rb,
lib/commit/helper.rb,
lib/commit/options.rb

Overview

Constant Summary collapse

QUIET =
0
NORMAL =
1
VERBOSE =
2
ANNOYING =
3
STFU =
4

Instance Method Summary collapse

Constructor Details

#initialize(default_branch: 'master') ⇒ GitCommit

Returns a new instance of GitCommit.



27
28
29
30
31
32
# File 'lib/commit/git_commit.rb', line 27

def initialize(default_branch: 'master')
  @branch = default_branch
  @commit_size = 0
  @gitignore_dirty = false
  @nh = ActiveSupport::NumberHelper
end

Instance Method Details

#add_recursively(name) ⇒ Object

Needs absolute path or the path relative to the current directory, not just the name of the directory



10
11
12
13
14
15
16
17
18
19
# File 'lib/commit/helper.rb', line 10

def add_recursively(name)
  Dir.entries(name).each do |entry|
    path = "#{name}/#{entry}"
    if File.directory(entry)
      scan_directory path
    else
      file_add path
    end
  end
end

#commit_push(msg = '-') ⇒ Object



34
35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/commit/git_commit.rb', line 34

def commit_push(msg = '-')
  puts msg.yellow if msg
  msg = @options[:commit_message] if @options[:commit_message]
  discover_branch

  puts "Committing with message '#{msg}'".green unless @options[:verbosity] == QUIET
  run "git commit -m '#{msg}' 2>&1 | sed -e '/^X11/d' -e '/^Warning:/d'", verbose: @options[:verbosity] >= VERBOSE
  # @repo.push 'origin', ['refs/heads/master'] # Needs a callback to handle authentication
  puts "Pushing to origin #{@branch}".green unless @options[:verbosity] == QUIET
  run "git push origin #{@branch} --tags 3>&1 1>&2 2>&3 | sed -e '/^X11/d' -e '/^Warning:/d'",
      verbose: @options[:verbosity] >= VERBOSE
  @change_count = 0
  @commit_size = 0
end

#discover_branchObject



49
50
51
52
53
54
55
56
57
# File 'lib/commit/git_commit.rb', line 49

def discover_branch
  if @repo.branches.entries.empty?
    # puts "\nYour git repository is empty. Please add at least one file before committing.".red
    # exit 4
    run "git branch -M #{@branch}"
  else
    @branch = @repo.head.name.sub(%r{^refs/heads/}, '')
  end
end

#escape(string) ⇒ Object

Handles single quotes, spaces and square braces in the given string



22
23
24
25
26
27
# File 'lib/commit/helper.rb', line 22

def escape(string)
  string.gsub("'", "\\\\'")
        .gsub(' ', '\\ ')
        .gsub('[', '\\[')
        .gsub(']', '\\]')
end

#file_add(filename) ⇒ Object

Parameters:

  • filename (String)

    Must be a path relative to the git root



30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'lib/commit/helper.rb', line 30

def file_add(filename)
  file_size = File.exist?(filename) ? File.size(filename) : 0
  if large_file?(filename)
    msg = <<~MESSAGE
      Not adding '#{filename}' because the git file size limit is #{@nh.to_human MAX_SIZE},
      however the file is #{@nh.to_human file_size}.
      The file will be added to .gitignore.
    MESSAGE
    puts msg.yellow unless @options[:verbosity] == QUIET

    newline = needs_newline('.gitignore') ? "\n" : ''
    File.write('.gitignore', "#{newline}#{filename}\n", mode: 'a')
    @gitignore_dirty = true
  elsif filename == '.gitignore'
    @gitignore_dirty = true
  else
    if @commit_size + file_size >= MAX_SIZE / 2.0
      # Defeat formatter
      commit_push 'A portion of the files to be committed is being pushed now because they are large.'
    end
    puts "Adding '#{escape filename}'".green unless @options[:verbosity] == QUIET
    run ['git', 'add', escape(filename)], verbose: @options[:verbosity] >= VERBOSE
  end
  @change_count += 1
  @commit_size += file_size
end

#git_project?Boolean

Returns:

  • (Boolean)


59
60
61
# File 'lib/commit/git_commit.rb', line 59

def git_project?
  run 'git rev-parse 2> /dev/null', verbose: false
end

#help(msg = nil) ⇒ Object



5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# File 'lib/commit/options.rb', line 5

def help(msg = nil)
  printf "Error: #{msg}\n\n".yellow unless msg.nil?
  msg = <<~HELP
    commit v#{Commit::VERSION}

    Runs git commit without prompting for a message.
    Files larger than #{@nh.to_human MAX_SIZE} are added to .gitignore instead of being committed.

    Usage: commit [options]
    Where options are:
      -a "tag message"
      -m "commit message"
      -v 0 # Minimum verbosity
      -v 1 # Default verbosity
      -v 2 # Maximum verbosity

    Examples:
      commit  # The default commit message is just a single dash (-)
      commit -v 0
      commit -m "This is a commit message"
      commit -v 0 -m "This is a commit message"
      commit -a 0.1.2

    This gem is further described in https://mslinn.com/git/1050-commit.html
  HELP
  puts msg.yellow
  exit 1
end

#large_file?(filename) ⇒ Boolean

Assumes .gitignore is never large

Returns:

  • (Boolean)


58
59
60
# File 'lib/commit/helper.rb', line 58

def large_file?(filename)
  File.size(filename) > MAX_SIZE if File.exist?(filename)
end

#large_filesObject



63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/commit/git_commit.rb', line 63

def large_files
  large = []
  @repo.status do |path, flags|
    puts "#{path} #{flags}" if @options[:verbosity] >= NORMAL
    if File(path).dir?
      scan_dir path
    elsif large_file?(filename)
      large << path.gsub(' ', '\ ').gsub('[', '\[').gsub(']', '\]')
    end
  end
  large
end

#mainObject



76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/commit/git_commit.rb', line 76

def main
  @options = parse_options
  repo_dir = ARGV.empty? ? Dir.pwd : ARGV[0]
  Dir.chdir(repo_dir) unless ARGV.empty?
  if git_project?
    process_tag # Exits if a tag was created
    @repo = Rugged::Repository.new repo_dir
    recursive_add
    commit_push if @commit_size.positive?
  else
    puts "Error: '#{repo_dir}' does not contain a git project".red
    exit 3
  end
end

#needs_newline(filename) ⇒ Object



62
63
64
65
66
67
# File 'lib/commit/helper.rb', line 62

def needs_newline(filename)
  return false unless File.exist? filename

  file_contents = File.read filename
  file_contents.nil? || file_contents.empty? || !file_contents.end_with?("\n")
end

#parse_optionsObject



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/commit/options.rb', line 34

def parse_options
  options = { message: '-', verbosity: 1 }
  OptionParser.new do |parser|
    parser.program_name = File.basename __FILE__
    @parser = parser

    parser.on('-a', '--tag TAG_MESSAGE', 'Specify tag message')
    parser.on('-m', '--message COMMIT_MESSAGE', 'Specify commit message')
    parser.on('-v', '--verbosity VERBOSITY', Integer, 'Verbosity (0..2)')

    parser.on_tail('-h', '--help', 'Show this message') do
      help
    end
  end.order!(into: options)
  help "Invalid verbosity value (#{options[:verbosity]})." if options[:verbosity].negative? || options[:verbosity] > 2
  if ARGV.length > 1
    help <<~END_MSG
      Incorrect syntax.
      After removing the options, the remaining command line was:
      #{ARGV.join ' '}
    END_MSG
  end
  options
end

#process_tagObject



91
92
93
94
95
96
97
98
# File 'lib/commit/git_commit.rb', line 91

def process_tag
  tag = @options[:tag]
  return unless tag

  run "git tag -a #{tag} -m 'v#{tag}'", verbose: @options[:verbosity] >= VERBOSE
  run 'git push origin --tags', verbose: @options[:verbosity] >= VERBOSE
  exit
end

#recursive_addObject

Exclude big files, git add all others



77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/commit/helper.rb', line 77

def recursive_add
  @change_count = 0
  run ['git', 'add', '--all'], verbose: @options[:verbosity] >= VERBOSE
  @repo.status do |path, flags|
    next if flags.include? :ignored

    if File.directory? path
      scan_directory path
    else
      file_add path
    end
  end
  if @gitignore_dirty
    puts 'Changing .gitignore'.green unless @options[:verbosity] == QUIET
    run 'git add .gitignore', verbose: @options[:verbosity] >= 2
    @change_count += 1
  end
  return unless @change_count.zero?

  puts 'No changes were detected to this git repository.'.green if @options[:verbosity] >= VERBOSE
  exit
end

#run(command, verbose: true, do_not_execute: false) ⇒ Object

Parameters:

  • command

    can be a String or an Array of String



101
102
103
104
105
106
107
108
109
# File 'lib/commit/git_commit.rb', line 101

def run(command, verbose: true, do_not_execute: false)
  if command.instance_of?(Array)
    puts command.join(' ') if verbose
    Kernel.system(*command) unless do_not_execute
  else
    puts command if verbose
    `#{command}`.chomp unless do_not_execute
  end
end

#scan_directory(path) ⇒ Object



100
101
102
103
104
105
106
107
108
109
# File 'lib/commit/helper.rb', line 100

def scan_directory(path)
  Dir.children(path) do |name|
    child_path = "#{path}/#{name}"
    if File.directory? child_path
      scan_directory child_path
    else
      file_add child_path
    end
  end
end