Skip to content

Commit 255e8e4

Browse files
Add worktree functionality
list, add, remove and prune Signed-off-by: Ofir Petrushka <hatkyinc@gmail.com>
1 parent 4bef5ab commit 255e8e4

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
@@ -308,6 +308,39 @@ 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+
340+
def worktree_prune
341+
command('worktree', ['prune'])
342+
end
343+
311344
def list_files(ref_dir)
312345
dir = File.join(@git_dir, 'refs', ref_dir)
313346
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.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#!/bin/sh
2+
#
3+
# An example hook script to check the commit log message taken by
4+
# applypatch from an e-mail message.
5+
#
6+
# The hook should exit with non-zero status after issuing an
7+
# appropriate message if it wants to stop the commit. The hook is
8+
# allowed to edit the commit message file.
9+
#
10+
# To enable this hook, make this file executable.
11+
12+
. git-sh-setup
13+
test -x "$GIT_DIR/hooks/commit-msg" &&
14+
exec "$GIT_DIR/hooks/commit-msg" ${1+"$@"}
15+
:
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#!/bin/sh
2+
#
3+
# An example hook script to check the commit log message.
4+
# Called by git-commit with one argument, the name of the file
5+
# that has the commit message. The hook should exit with non-zero
6+
# status after issuing an appropriate message if it wants to stop the
7+
# commit. The hook is allowed to edit the commit message file.
8+
#
9+
# To enable this hook, make this file executable.
10+
11+
# Uncomment the below to add a Signed-off-by line to the message.
12+
# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p')
13+
# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1"
14+
15+
# This example catches duplicate Signed-off-by lines.
16+
17+
test "" = "$(grep '^Signed-off-by: ' "$1" |
18+
sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || {
19+
echo >&2 Duplicate Signed-off-by lines.
20+
exit 1
21+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#!/bin/sh
2+
#
3+
# An example hook script that is called after a successful
4+
# commit is made.
5+
#
6+
# To enable this hook, make this file executable.
7+
8+
: Nothing
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#!/bin/sh
2+
#
3+
# An example hook script for the post-receive event
4+
#
5+
# This script is run after receive-pack has accepted a pack and the
6+
# repository has been updated. It is passed arguments in through stdin
7+
# in the form
8+
# <oldrev> <newrev> <refname>
9+
# For example:
10+
# aa453216d1b3e49e7f6f98441fa56946ddcd6a20 68f7abf4e6f922807889f52bc043ecd31b79f814 refs/heads/master
11+
#
12+
# see contrib/hooks/ for an sample, or uncomment the next line (on debian)
13+
#
14+
15+
16+
#. /usr/share/doc/git-core/contrib/hooks/post-receive-email
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#!/bin/sh
2+
#
3+
# An example hook script to prepare a packed repository for use over
4+
# dumb transports.
5+
#
6+
# To enable this hook, make this file executable by "chmod +x post-update".
7+
8+
exec git-update-server-info
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#!/bin/sh
2+
#
3+
# An example hook script to verify what is about to be committed
4+
# by applypatch from an e-mail message.
5+
#
6+
# The hook should exit with non-zero status after issuing an
7+
# appropriate message if it wants to stop the commit.
8+
#
9+
# To enable this hook, make this file executable.
10+
11+
. git-sh-setup
12+
test -x "$GIT_DIR/hooks/pre-commit" &&
13+
exec "$GIT_DIR/hooks/pre-commit" ${1+"$@"}
14+
:
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
#!/bin/sh
2+
#
3+
# An example hook script to verify what is about to be committed.
4+
# Called by git-commit with no arguments. The hook should
5+
# exit with non-zero status after issuing an appropriate message if
6+
# it wants to stop the commit.
7+
#
8+
# To enable this hook, make this file executable.
9+
10+
# This is slightly modified from Andrew Morton's Perfect Patch.
11+
# Lines you introduce should not have trailing whitespace.
12+
# Also check for an indentation that has SP before a TAB.
13+
14+
if git-rev-parse --verify HEAD 2>/dev/null
15+
then
16+
git-diff-index -p -M --cached HEAD
17+
else
18+
# NEEDSWORK: we should produce a diff with an empty tree here
19+
# if we want to do the same verification for the initial import.
20+
:
21+
fi |
22+
perl -e '
23+
my $found_bad = 0;
24+
my $filename;
25+
my $reported_filename = "";
26+
my $lineno;
27+
sub bad_line {
28+
my ($why, $line) = @_;
29+
if (!$found_bad) {
30+
print STDERR "*\n";
31+
print STDERR "* You have some suspicious patch lines:\n";
32+
print STDERR "*\n";
33+
$found_bad = 1;
34+
}
35+
if ($reported_filename ne $filename) {
36+
print STDERR "* In $filename\n";
37+
$reported_filename = $filename;
38+
}
39+
print STDERR "* $why (line $lineno)\n";
40+
print STDERR "$filename:$lineno:$line\n";
41+
}
42+
while (<>) {
43+
if (m|^diff --git a/(.*) b/\1$|) {
44+
$filename = $1;
45+
next;
46+
}
47+
if (/^@@ -\S+ \+(\d+)/) {
48+
$lineno = $1 - 1;
49+
next;
50+
}
51+
if (/^ /) {
52+
$lineno++;
53+
next;
54+
}
55+
if (s/^\+//) {
56+
$lineno++;
57+
chomp;
58+
if (/\s$/) {
59+
bad_line("trailing whitespace", $_);
60+
}
61+
if (/^\s* /) {
62+
bad_line("indent SP followed by a TAB", $_);
63+
}
64+
if (/^(?:[<>=]){7}/) {
65+
bad_line("unresolved merge conflict", $_);
66+
}
67+
}
68+
}
69+
exit($found_bad);
70+
'

0 commit comments

Comments
 (0)