Skip to content

Commit d1908f6

Browse files
hatkyinc2ofir-petrushkajcouball
authored
Add worktree functionality (ruby-git#479)
list, add, remove and prune Signed-off-by: Ofir Petrushka <hatkyinc@gmail.com> Co-authored-by: Ofir Petrushka <hatkyinc@gmail.com> Co-authored-by: James Couball <jcouball@yahoo.com>
1 parent 896e31d commit d1908f6

File tree

279 files changed

+897
-2
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

279 files changed

+897
-2
lines changed

README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ like:
4141

4242
`@git.log(20).object("some_file").since("2 weeks ago").between('v2.6', 'v2.7').each { |commit| [block] }`
4343

44+
**Git::Worktrees** - Enumerable object that holds `Git::Worktree objects`.
45+
4446
## Examples
4547

4648
Here are a bunch of examples of how to use the Ruby/Git package.
@@ -145,6 +147,14 @@ Here are the operations that need read permission only.
145147
puts file_diff.blob(:src).contents
146148
end
147149

150+
g.worktrees # returns Git::Worktree objects
151+
g.worktrees.count
152+
g.worktrees.each do |worktree|
153+
worktree.dir
154+
worktree.gcommit
155+
worktree.to_s
156+
end
157+
148158
g.config('user.name') # returns 'Scott Chacon'
149159
g.config # returns whole config hash
150160

@@ -252,6 +262,11 @@ And here are the operations that will need to write to your git repository.
252262

253263
g.push
254264
g.push(g.remote('name'))
265+
266+
g.worktree('/tmp/new_worktree').add
267+
g.worktree('/tmp/new_worktree', 'branch1').add
268+
g.worktree('/tmp/new_worktree').remove
269+
g.worktrees.prune
255270
```
256271

257272
Some examples of more low-level index and tree operations

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: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,39 @@ def branches_all
310310
arr
311311
end
312312

313+
def worktrees_all
314+
arr = []
315+
directory = ''
316+
# Output example for `worktree list --porcelain`:
317+
# worktree /code/public/ruby-git
318+
# HEAD 4bef5abbba073c77b4d0ccc1ffcd0ed7d48be5d4
319+
# branch refs/heads/master
320+
#
321+
# worktree /tmp/worktree-1
322+
# HEAD b8c63206f8d10f57892060375a86ae911fad356e
323+
# detached
324+
#
325+
command_lines('worktree',['list', '--porcelain']).each do |w|
326+
s = w.split("\s")
327+
directory = s[1] if s[0] == 'worktree'
328+
arr << [directory, s[1]] if s[0] == 'HEAD'
329+
end
330+
arr
331+
end
332+
333+
def worktree_add(dir, commitish = nil)
334+
return command('worktree', ['add', dir, commitish]) if !commitish.nil?
335+
command('worktree', ['add', dir])
336+
end
337+
338+
def worktree_remove(dir)
339+
command('worktree', ['remove', dir])
340+
end
341+
342+
def worktree_prune
343+
command('worktree', ['prune'])
344+
end
345+
313346
def list_files(ref_dir)
314347
dir = File.join(@git_dir, 'refs', ref_dir)
315348
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: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
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+
43+
def prune
44+
@base.lib.worktree_prune
45+
end
46+
end
47+
end
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
545ffc79786f268524c35e1e05b1770c7c74faf1 not-for-merge branch 'master' of ../working

tests/files/worktree/dot_git/HEAD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
ref: refs/heads/git_grep

tests/files/worktree/dot_git/config

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
[user]
2+
name = Scott Chacon
3+
email = schacon@gmail.com
4+
[commit]
5+
gpgsign = false
6+
[core]
7+
repositoryformatversion = 0
8+
filemode = true
9+
bare = false
10+
logallrefupdates = true
11+
[gui]
12+
geometry = 986x682+365+124 211 500
13+
[remote "working"]
14+
url = ../working.git
15+
fetch = +refs/heads/*:refs/remotes/working/*
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Unnamed repository; edit this file to name it for gitweb.

0 commit comments

Comments
 (0)