Skip to content

Commit eee23c3

Browse files
Add worktree listing, add and remove
Signed-off-by: Ofir Petrushka <hatkyinc@gmail.com>
1 parent 4bef5ab commit eee23c3

File tree

16 files changed

+185
-4
lines changed

16 files changed

+185
-4
lines changed

lib/git.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
require 'git/stashes'
2222
require 'git/version'
2323
require 'git/working_directory'
24+
require 'git/worktree'
25+
require 'git/worktrees'
2426

2527
lib = Git::Lib.new(nil, nil)
2628
unless lib.meets_required_version?

lib/git/base/factory.rb

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ module Git
33
class Base
44

55
module Factory
6-
6+
77
# returns a Git::Branch object for branch_name
88
def branch(branch_name = 'master')
99
Git::Branch.new(self, branch_name)
@@ -14,7 +14,18 @@ def branch(branch_name = 'master')
1414
def branches
1515
Git::Branches.new(self)
1616
end
17-
17+
18+
# returns a Git::Worktree object for dir, commitish
19+
def worktree(dir, commitish = nil)
20+
Git::Worktree.new(self, dir, commitish)
21+
end
22+
23+
# returns a Git::worktrees object of all the Git::Worktrees
24+
# objects for this repo
25+
def worktrees
26+
Git::Worktrees.new(self)
27+
end
28+
1829
def commit_tree(tree = nil, opts = {})
1930
Git::Object::Commit.new(self, self.lib.commit_tree(tree, opts))
2031
end

lib/git/lib.rb

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,35 @@ def branches_all
308308
arr
309309
end
310310

311+
def worktrees_all
312+
arr = []
313+
directory = ''
314+
# Output example for `worktree list --porcelain`:
315+
# worktree /code/public/ruby-git
316+
# HEAD 4bef5abbba073c77b4d0ccc1ffcd0ed7d48be5d4
317+
# branch refs/heads/master
318+
#
319+
# worktree /tmp/worktree-1
320+
# HEAD b8c63206f8d10f57892060375a86ae911fad356e
321+
# detached
322+
#
323+
command_lines('worktree',['list', '--porcelain']).each do |w|
324+
s = w.split("\s")
325+
directory = s[1] if s[0] == 'worktree'
326+
arr << [directory, s[1]] if s[0] == 'HEAD'
327+
end
328+
arr
329+
end
330+
331+
def worktree_add(dir, commitish = nil)
332+
return command('worktree', ['add', dir, commitish]) if !commitish.nil?
333+
command('worktree', ['add', dir])
334+
end
335+
336+
def worktree_remove(dir)
337+
command('worktree', ['remove', dir])
338+
end
339+
311340
def list_files(ref_dir)
312341
dir = File.join(@git_dir, 'refs', ref_dir)
313342
files = []

lib/git/worktree.rb

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
require 'git/path'
2+
3+
module Git
4+
5+
class Worktree < Path
6+
7+
attr_accessor :full, :dir, :gcommit
8+
9+
def initialize(base, dir, gcommit = nil)
10+
@full = dir
11+
@full += ' ' + gcommit if !gcommit.nil?
12+
@base = base
13+
@dir = dir
14+
@gcommit = gcommit
15+
end
16+
17+
def gcommit
18+
@gcommit ||= @base.gcommit(@full)
19+
@gcommit
20+
end
21+
22+
def add
23+
@base.lib.worktree_add(@dir, @gcommit)
24+
end
25+
26+
def remove
27+
@base.lib.worktree_remove(@dir)
28+
end
29+
30+
def to_a
31+
[@full]
32+
end
33+
34+
def to_s
35+
@full
36+
end
37+
end
38+
end

lib/git/worktrees.rb

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
module Git
2+
# object that holds all the available worktrees
3+
class Worktrees
4+
5+
include Enumerable
6+
7+
def initialize(base)
8+
@worktrees = {}
9+
10+
@base = base
11+
12+
# Array contains [dir, git_hash]
13+
@base.lib.worktrees_all.each do |w|
14+
@worktrees[w[0]] = Git::Worktree.new(@base, w[0], w[1])
15+
end
16+
end
17+
18+
# array like methods
19+
20+
def size
21+
@worktrees.size
22+
end
23+
24+
def each(&block)
25+
@worktrees.values.each(&block)
26+
end
27+
28+
def [](worktree_name)
29+
@worktrees.values.inject(@worktrees) do |worktrees, worktree|
30+
worktrees[worktree.full] ||= worktree
31+
worktrees
32+
end[worktree_name.to_s]
33+
end
34+
35+
def to_s
36+
out = ''
37+
@worktrees.each do |k, b|
38+
out << b.to_s << "\n"
39+
end
40+
out
41+
end
42+
end
43+
end

tests/files/working/dot_git/index

0 Bytes
Binary file not shown.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
0000000000000000000000000000000000000000 5e53019b3238362144c2766f02a2c00d91fcc023 Scott Chacon <schacon@gmail.com> 1596189348 +1000 branch: Created from HEAD
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
5e53019b3238362144c2766f02a2c00d91fcc023
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
935badc874edd62a8629aaf103418092c73f0a56
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
5e53019b3238362144c2766f02a2c00d91fcc023
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../..
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/tmp/aaa/.git
446 Bytes
Binary file not shown.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
5e53019b3238362144c2766f02a2c00d91fcc023 5e53019b3238362144c2766f02a2c00d91fcc023 Scott Chacon <schacon@gmail.com> 1596189348 +1000 reset: moving to HEAD
2+
5e53019b3238362144c2766f02a2c00d91fcc023 935badc874edd62a8629aaf103418092c73f0a56 Scott Chacon <schacon@gmail.com> 1596189368 +1000 checkout: moving from aaa to 935badc874edd62a8629aaf103418092c73f0a56

tests/units/test_logger.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ def test_logger
2020

2121
logc = File.read(log.path)
2222
assert(/INFO -- : git '--git-dir=[^']+' '--work-tree=[^']+' '-c' 'color.ui=false' branch '-a'/.match(logc))
23-
assert(/DEBUG -- : diff_over_patches/.match(logc))
23+
assert(/DEBUG -- : aaa\n diff_over_patches/.match(logc))
2424

2525
log = Tempfile.new('logfile')
2626
log.close
@@ -32,7 +32,7 @@ def test_logger
3232

3333
logc = File.read(log.path)
3434
assert(/INFO -- : git '--git-dir=[^']+' '--work-tree=[^']+' '-c' 'color.ui=false' branch '-a'/.match(logc))
35-
assert(!/DEBUG -- : diff_over_patches/.match(logc))
35+
assert(!/DEBUG -- : aaa\n diff_over_patches/.match(logc))
3636
end
3737

3838
end

tests/units/test_worktree.rb

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
#!/usr/bin/env ruby
2+
3+
require File.dirname(__FILE__) + '/../test_helper'
4+
5+
SAMPLE_LAST_COMMIT = '5e53019b3238362144c2766f02a2c00d91fcc023'
6+
7+
class TestWorktree < Test::Unit::TestCase
8+
def setup
9+
set_file_paths
10+
@git = Git.open(@wdir)
11+
12+
@commit = @git.object('1cc8667014381')
13+
@tree = @git.object('1cc8667014381^{tree}')
14+
@blob = @git.object('v2.5:example.txt')
15+
16+
@worktrees = @git.worktrees
17+
end
18+
19+
def test_worktrees_all
20+
assert(@git.worktrees.is_a?(Git::Worktrees))
21+
assert(@git.worktrees.first.is_a?(Git::Worktree))
22+
assert_equal(@git.worktrees.size, 2)
23+
end
24+
25+
def test_worktrees_single
26+
worktree = @git.worktrees.first
27+
assert_equal(worktree.dir, @git.dir.to_s)
28+
assert_equal(worktree.gcommit, SAMPLE_LAST_COMMIT)
29+
end
30+
31+
def test_worktree_add_and_remove
32+
assert_equal(@git.worktrees.size, 2)
33+
34+
@git.worktree('/tmp/pp1').add
35+
assert_equal(@git.worktrees.size, 3)
36+
@git.worktree('/tmp/pp1').remove
37+
assert_equal(@git.worktrees.size, 2)
38+
39+
@git.worktree('/tmp/pp2', 'gitsearch1').add
40+
@git.worktree('/tmp/pp2').remove
41+
42+
@git.worktree('/tmp/pp3', '34a566d193dc4702f03149969a2aad1443231560').add
43+
@git.worktree('/tmp/pp3').remove
44+
45+
@git.worktree('/tmp/pp4', 'test_object').add
46+
@git.worktree('/tmp/pp4').remove
47+
48+
assert_equal(@git.worktrees.size, 2)
49+
end
50+
end

0 commit comments

Comments
 (0)