Class: GitTrim

Inherits:
Object
  • Object
show all
Defined in:
lib/git-trim.rb,
lib/git-trim/version.rb

Constant Summary collapse

VERSION =
"0.2.0"

Instance Method Summary collapse

Instance Method Details

#base_refObject

Local main when it exists; otherwise origin/main (e.g. bare-repo + worktrees layouts where no local main is checked out). Nil if neither.



52
53
54
55
56
57
# File 'lib/git-trim.rb', line 52

def base_ref
  ["main", "origin/main"].find do |ref|
    %x{git rev-parse --verify --quiet #{Shellwords.escape(ref)}}
    $?.success?
  end
end

#current_branchObject



46
47
48
# File 'lib/git-trim.rb', line 46

def current_branch
  %x{git branch --show-current}.strip
end

#find_file(filename, directory = Dir.pwd) ⇒ Object



70
71
72
73
74
75
76
77
78
79
# File 'lib/git-trim.rb', line 70

def find_file(filename, directory=Dir.pwd)
  local_filename = File.expand_path(filename, directory)
  if File.exist?(local_filename)
    return local_filename
  elsif directory == "/"
    return nil
  else
    return find_file(filename, File.expand_path("..", directory))
  end
end

#linked_worktreesObject

Maps branch name => worktree path for linked worktrees. The first entry in ‘git worktree list` is the main worktree, which is never removable.



61
62
63
64
65
66
67
68
# File 'lib/git-trim.rb', line 61

def linked_worktrees
  entries = %x{git worktree list --porcelain}.split("\n\n")
  entries.drop(1).each_with_object({}) do |entry, map|
    path = entry[/^worktree (.+)$/, 1]
    branch = entry[/^branch refs\/heads\/(.+)$/, 1]
    map[branch] = path if path && branch
  end
end

#run(argv = 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
33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/git-trim.rb', line 5

def run(argv=nil)
  %x{git fetch -ap}

  if File.exist?(".git-protected-branches")
    protected_branches = File.read(".git-protected-branches").split("\n")
  end
  protected_branches ||= []
  protected_branches << "main"

  base = base_ref
  unless base
    warn "git-trim: neither 'main' nor 'origin/main' exists; nothing to trim against"
    return 1
  end

  branches = %x{git branch --merged #{Shellwords.escape(base)} --format='%(refname:short)'}.split("\n").collect(&:strip)
  branches -= protected_branches
  branches -= [current_branch]

  worktrees = linked_worktrees

  branches.each do |branch|
    if (path = worktrees[branch])
      output = %x{git worktree remove #{Shellwords.escape(path)} 2>&1}
      unless $?.success?
        if output.include?("contains modified or untracked files")
          puts "Skipping branch '#{branch}': worktree at #{path} has local changes"
        else
          puts "Skipping branch '#{branch}': could not remove worktree at #{path}"
          puts output
        end
        next
      end
      puts "Removed worktree #{path}"
    end
    puts %x{git branch -d #{Shellwords.escape(branch)}}
  end

  0
end