twit 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +3 -0
- data/README.markdown +79 -4
- data/lib/twit.rb +88 -1
- data/lib/twit/cli.rb +129 -10
- data/lib/twit/error.rb +3 -0
- data/lib/twit/repo.rb +171 -1
- data/lib/twit/version.rb +1 -1
- data/spec/spec_helper.rb +15 -0
- data/spec/twit/cli_spec.rb +32 -16
- data/spec/twit/repo_spec.rb +295 -2
- data/spec/twit_spec.rb +158 -0
- data/twit.gemspec +2 -2
- metadata +6 -10
- data/lib/twit/init.rb +0 -21
- data/lib/twit/repo/save.rb +0 -23
- data/spec/twit/init_spec.rb +0 -43
- data/spec/twit/repo/save_spec.rb +0 -55
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 85fc88fe59a3282afb1f4e29af583408a35a3f29
|
4
|
+
data.tar.gz: a30902725f73eeeb3ca2b84666ef23908292e265
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e82495bbc0018af33c6f33ae5ea9e14ebedd0a35c95ee6435fd6a0d0c227162dd76ebf93e70ab3f76687c481fd91d9979edf8024e50121da5c9da098b1d41812
|
7
|
+
data.tar.gz: 4a295fe77e361dd9769a72fe10494c50d9be7dcc4c6a854a5c2bfbf944ab59a6075e8d9e1e5229eb22ee8b33efab87a5e7e6c38034b1f7b4fdcd670b89a4ab51
|
data/.gitignore
CHANGED
data/README.markdown
CHANGED
@@ -1,6 +1,39 @@
|
|
1
|
-
# Twit:
|
1
|
+
# Twit: training wheels for Git
|
2
2
|
|
3
|
-
Twit
|
3
|
+
Twit is a wrapper for [Git](http://git-scm.com) that abstracts concepts that
|
4
|
+
many newcomers find tedious or confusing.
|
5
|
+
|
6
|
+
When explaining version control to newcomers, the benefits are often unclear
|
7
|
+
amid the complicated rules and syntax of a powerful tool like Git. Twit aims to
|
8
|
+
provide an *easy and functional* demonstration of the usefulness of a branching
|
9
|
+
version control system.
|
10
|
+
|
11
|
+
For example, you can say that "version control allows you to save snapshots of
|
12
|
+
your project history." However, in order to do this, you need to understand
|
13
|
+
Git's two-step stage-then-commit workflow, with all its corner cases regarding
|
14
|
+
new/deleted/moved files.
|
15
|
+
|
16
|
+
Instead, Twit exposes a simple command to create a new commit with a snapshot
|
17
|
+
of the repository:
|
18
|
+
|
19
|
+
twit save
|
20
|
+
|
21
|
+
This stages any changes (including new files and deletions) and creates a
|
22
|
+
commit, prompting the user for a commit message if needed.
|
23
|
+
|
24
|
+
To create a new branch (and save any changes to the new branch as well):
|
25
|
+
|
26
|
+
twit saveas my_new_branch
|
27
|
+
|
28
|
+
This quick-and-easy approach allows a new user to get started using version
|
29
|
+
control right away, without having to learn Git's minutiae.
|
30
|
+
|
31
|
+
However, this simple program is **not** meant to replace Git for the power
|
32
|
+
user. The interface was designed to be user-friendly at the cost of
|
33
|
+
flexibility. If you are a programmer, you should probably just buckle down and
|
34
|
+
[learn git](http://gitref.org). Instead, Twit serves as an introduction to
|
35
|
+
version control for users that would probably never learn Git, like writers or
|
36
|
+
students.
|
4
37
|
|
5
38
|
## Installation
|
6
39
|
|
@@ -18,14 +51,53 @@ To install, simply run:
|
|
18
51
|
|
19
52
|
Initialize a new git repository in the current directory.
|
20
53
|
|
21
|
-
|
54
|
+
Equivalent to: `git init`
|
55
|
+
|
56
|
+
### `save` -- commit all new changes to the current branch
|
22
57
|
|
23
|
-
twit save
|
58
|
+
twit save [DESCRIBE_CHANGES]
|
24
59
|
|
25
60
|
Take a snapshot of all files in the directory.
|
26
61
|
|
27
62
|
Any changes on the working tree will be committed to the current branch.
|
28
63
|
|
64
|
+
Equivalent to: `git add --all && git commit -m <DESCRIBE_CHANGES>`
|
65
|
+
|
66
|
+
### `saveas` -- commit all new changes to a new branch
|
67
|
+
|
68
|
+
twit saveas [NEW_BRANCH] [DESCRIBE_CHANGES]
|
69
|
+
|
70
|
+
Equivalent to: `git checkout -b <NEW_BRANCH>` then `twit save`
|
71
|
+
|
72
|
+
### `open` -- open another branch
|
73
|
+
|
74
|
+
twit open [BRANCH]
|
75
|
+
|
76
|
+
Equivalent to: `git checkout <branch>`
|
77
|
+
|
78
|
+
### `include` -- incorperate changes from another branch
|
79
|
+
|
80
|
+
twit include [OTHER_BRANCH]
|
81
|
+
|
82
|
+
Incorperate changes from another branch, but do not save them yet. (The user
|
83
|
+
can resolve any conflicts and then run `twit save` themselves.)
|
84
|
+
|
85
|
+
Equivalent to: `git merge --no-ff --no-commit [OTHER_BRANCH]`
|
86
|
+
|
87
|
+
### `discard` -- permanently delete unsaved changes
|
88
|
+
|
89
|
+
twit discard
|
90
|
+
|
91
|
+
**Permanently** delete any unsaved changes to the current branch. Be careful!
|
92
|
+
|
93
|
+
Equivalent to: `git reset --hard`
|
94
|
+
|
95
|
+
### `list` -- show all branches
|
96
|
+
|
97
|
+
twit list
|
98
|
+
|
99
|
+
Equivalent to: `git branch`
|
100
|
+
|
29
101
|
## API
|
30
102
|
|
31
103
|
All command-line functions are available for use as a Ruby library as well.
|
@@ -41,6 +113,9 @@ All command-line functions are available for use as a Ruby library as well.
|
|
41
113
|
# Take a snapshot
|
42
114
|
Twit.save "Add some foo"
|
43
115
|
|
116
|
+
# Create a new branch
|
117
|
+
Twit.saveas "feature-branch"
|
118
|
+
|
44
119
|
## Development
|
45
120
|
|
46
121
|
### Setup
|
data/lib/twit.rb
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
require "twit/version"
|
2
2
|
require "twit/repo"
|
3
|
-
require "twit/init"
|
4
3
|
require "twit/error"
|
5
4
|
|
5
|
+
require 'open3'
|
6
|
+
|
6
7
|
# This module exposes twit commands as methods.
|
7
8
|
module Twit
|
8
9
|
|
@@ -12,4 +13,90 @@ module Twit
|
|
12
13
|
Twit::Repo.new
|
13
14
|
end
|
14
15
|
|
16
|
+
# Initialize a git repository in a directory. Return a Twit::Repo object
|
17
|
+
# representing the new repository.
|
18
|
+
#
|
19
|
+
# If no argument is supplied, use the working directory.
|
20
|
+
#
|
21
|
+
# If init is called on a directory that is already part of a repository,
|
22
|
+
# simply do nothing.
|
23
|
+
def self.init dir = nil
|
24
|
+
dir ||= Dir.getwd
|
25
|
+
|
26
|
+
if is_repo? dir
|
27
|
+
return
|
28
|
+
end
|
29
|
+
|
30
|
+
Dir.chdir dir do
|
31
|
+
stdout, stderr, status = Open3.capture3 "git init"
|
32
|
+
if status != 0
|
33
|
+
raise Error, stderr
|
34
|
+
end
|
35
|
+
end
|
36
|
+
Repo.new dir
|
37
|
+
end
|
38
|
+
|
39
|
+
# Check if a given directory is a git repository.
|
40
|
+
#
|
41
|
+
# If no argument is supplied, use the working directory.
|
42
|
+
def self.is_repo? dir = nil
|
43
|
+
dir ||= Dir.getwd
|
44
|
+
Dir.chdir dir do
|
45
|
+
stdout, stderr, status = Open3.capture3 "git status"
|
46
|
+
if status != 0
|
47
|
+
if /Not a git repository/.match stderr
|
48
|
+
return false
|
49
|
+
else
|
50
|
+
raise Error, stderr
|
51
|
+
end
|
52
|
+
end
|
53
|
+
return true
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# See {Twit::Repo#save}.
|
58
|
+
def self.save message
|
59
|
+
self.repo.save message
|
60
|
+
end
|
61
|
+
|
62
|
+
# See {Twit::Repo#saveas}.
|
63
|
+
def self.saveas branch, message = nil
|
64
|
+
self.repo.saveas branch, message
|
65
|
+
end
|
66
|
+
|
67
|
+
# See {Twit::Repo#discard}. (WARNING: PERMANENTLY DESTROYS DATA!)
|
68
|
+
def self.discard
|
69
|
+
self.repo.discard
|
70
|
+
end
|
71
|
+
|
72
|
+
# See {Twit::Repo#open}.
|
73
|
+
def self.open branch
|
74
|
+
self.repo.open branch
|
75
|
+
end
|
76
|
+
|
77
|
+
# See {Twit::Repo#include}.
|
78
|
+
def self.include branch
|
79
|
+
self.repo.include branch
|
80
|
+
end
|
81
|
+
|
82
|
+
# See {Twit::Repo#include_into}.
|
83
|
+
def self.include_into branch
|
84
|
+
self.repo.include_into branch
|
85
|
+
end
|
86
|
+
|
87
|
+
# See {Twit::Repo#list}.
|
88
|
+
def self.list
|
89
|
+
self.repo.list
|
90
|
+
end
|
91
|
+
|
92
|
+
# See {Twit::Repo#current_branch}.
|
93
|
+
def self.current_branch
|
94
|
+
self.repo.current_branch
|
95
|
+
end
|
96
|
+
|
97
|
+
# See {Twit::Repo#nothing_to_commit?}.
|
98
|
+
def self.nothing_to_commit?
|
99
|
+
self.repo.nothing_to_commit?
|
100
|
+
end
|
101
|
+
|
15
102
|
end
|
data/lib/twit/cli.rb
CHANGED
@@ -5,24 +5,143 @@ module Twit
|
|
5
5
|
|
6
6
|
# Automatically-built command-line interface (using Thor).
|
7
7
|
class CLI < Thor
|
8
|
+
include Thor::Actions
|
8
9
|
|
9
10
|
desc "init", "Create an empty repository in the current directory"
|
10
|
-
#
|
11
|
+
# See {Twit::init}
|
11
12
|
def init
|
12
|
-
|
13
|
+
begin
|
14
|
+
if Twit.is_repo?
|
15
|
+
say "You are already in a repository! (Root directory: #{Twit.repo.root})"
|
16
|
+
return
|
17
|
+
end
|
18
|
+
Twit.init
|
19
|
+
rescue Error => e
|
20
|
+
say "Error: #{e.message}"
|
21
|
+
end
|
13
22
|
end
|
14
23
|
|
15
|
-
desc "save
|
16
|
-
#
|
24
|
+
desc "save [DESCRIBE_CHANGES]", "Take a snapshot of all files"
|
25
|
+
# See {Twit::Repo#save}.
|
17
26
|
def save message = nil
|
18
|
-
|
19
|
-
|
20
|
-
|
27
|
+
begin
|
28
|
+
if Twit.nothing_to_commit?
|
29
|
+
say "No new edits to save"
|
30
|
+
return
|
31
|
+
end
|
32
|
+
while message.nil? || message.strip == ""
|
33
|
+
message = ask "Please supply a message describing your changes:"
|
34
|
+
end
|
35
|
+
Twit.save message
|
36
|
+
rescue Error => e
|
37
|
+
say "Error: #{e.message}"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
desc "saveas [NEW_BRANCH] [DESCRIBE_CHANGES]", "Save snapshot to new branch"
|
42
|
+
# See {Twit::Repo#saveas}.
|
43
|
+
def saveas branch = nil, message = nil
|
44
|
+
while branch.nil? || branch.strip == ""
|
45
|
+
branch = ask "Please supply the name for your new branch:"
|
46
|
+
end
|
47
|
+
begin
|
48
|
+
if (not Twit.nothing_to_commit?) && message.nil?
|
49
|
+
message = ask "Please supply a message describing your changes:"
|
50
|
+
end
|
51
|
+
begin
|
52
|
+
Twit.saveas branch, message
|
53
|
+
rescue InvalidParameter => e
|
54
|
+
if /already exists/.match e.message
|
55
|
+
say "Cannot saveas to existing branch. See \"twit help include_into\""
|
56
|
+
else
|
57
|
+
say "Error: #{e.message}"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
rescue Error => e
|
61
|
+
say "Error: #{e.message}"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
desc "open [BRANCH]", "Open a branch"
|
66
|
+
# See {Twit::Repo#open}.
|
67
|
+
def open branch = nil
|
68
|
+
while branch.nil? || branch.strip == ""
|
69
|
+
branch = ask "Please enter the name of the branch to open:"
|
70
|
+
end
|
71
|
+
begin
|
72
|
+
Twit.open branch
|
73
|
+
rescue Error => e
|
74
|
+
say "Error: #{e.message}"
|
75
|
+
else
|
76
|
+
say "Opened #{branch}."
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
desc "list", "Display a list of branches"
|
81
|
+
# See {Twit::Repo#list} and {Twit::Repo#current_branch}
|
82
|
+
def list
|
83
|
+
begin
|
84
|
+
@current = Twit.current_branch
|
85
|
+
@others = Twit.list.reject { |b| b == @current }
|
86
|
+
rescue Error => e
|
87
|
+
say "Error: #{e.message}"
|
88
|
+
return
|
89
|
+
end
|
90
|
+
say "Current branch: #{@current}"
|
91
|
+
say "Other branches:"
|
92
|
+
@others.each do |branch|
|
93
|
+
say "- #{branch}"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
desc "include", "Integrate changes from another branch"
|
98
|
+
# See {Twit::Repo#include}.
|
99
|
+
def include other_branch = nil
|
100
|
+
while other_branch.nil? || other_branch.strip == ""
|
101
|
+
other_branch = ask "Please enter the name of the branch to pull changes from:"
|
102
|
+
end
|
103
|
+
begin
|
104
|
+
@success = Twit.include other_branch
|
105
|
+
rescue Error => e
|
106
|
+
say "Error: #{e.message}"
|
107
|
+
return
|
108
|
+
end
|
109
|
+
if @success
|
110
|
+
say "Changes from #{other_branch} successfully included into #{Twit.current_branch}."
|
111
|
+
say "Use \"twit save\" to save them."
|
112
|
+
else
|
113
|
+
say "Conflicts detected! Fix them, then use \"twit save\" to save the changes."
|
21
114
|
end
|
115
|
+
end
|
116
|
+
|
117
|
+
desc "include_into", "Integrate changes into another branch"
|
118
|
+
# See {Twit::Repo#include}.
|
119
|
+
def include_into other_branch = nil
|
120
|
+
while other_branch.nil? || other_branch.strip == ""
|
121
|
+
other_branch = ask "Please enter the name of the branch to push changes into:"
|
122
|
+
end
|
123
|
+
begin
|
124
|
+
@original = Twit.current_branch
|
125
|
+
@success = Twit.include_into other_branch
|
126
|
+
rescue Error => e
|
127
|
+
say "Error: #{e.message}"
|
128
|
+
return
|
129
|
+
end
|
130
|
+
if @success
|
131
|
+
say "Changes from #{@original} successfully included into #{other_branch}."
|
132
|
+
say "Use \"twit save\" to save them."
|
133
|
+
else
|
134
|
+
say "Conflicts detected! Fix them, then use \"twit save\" to save the changes."
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
desc "discard", "PERMANTENTLY delete all changes since last save"
|
139
|
+
# See {Twit::Repo#discard}.
|
140
|
+
def discard
|
22
141
|
begin
|
23
|
-
Twit.
|
24
|
-
rescue
|
25
|
-
|
142
|
+
Twit.discard
|
143
|
+
rescue Error => e
|
144
|
+
say "Error: #{e.message}"
|
26
145
|
end
|
27
146
|
end
|
28
147
|
|
data/lib/twit/error.rb
CHANGED
data/lib/twit/repo.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
require 'open3'
|
2
2
|
require 'twit/error'
|
3
|
-
require 'twit/repo/save'
|
4
3
|
|
5
4
|
module Twit
|
6
5
|
|
@@ -30,6 +29,177 @@ module Twit
|
|
30
29
|
end
|
31
30
|
@root = root
|
32
31
|
end
|
32
|
+
|
33
|
+
# Update the snapshot of the current directory.
|
34
|
+
def save message
|
35
|
+
Dir.chdir @root do
|
36
|
+
cmd = "git add --all && git commit -m \"#{message}\""
|
37
|
+
stdout, stderr, status = Open3.capture3 cmd
|
38
|
+
if status != 0
|
39
|
+
if /nothing to commit/.match stdout
|
40
|
+
raise NothingToCommitError
|
41
|
+
elsif /Not a git repository/.match stderr
|
42
|
+
raise NotARepositoryError
|
43
|
+
else
|
44
|
+
raise Error, stderr
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# Save the current state of the repository to a new branch.
|
51
|
+
def saveas newbranch, message = nil
|
52
|
+
message ||= "Create new branch: #{newbranch}"
|
53
|
+
# First, create the new branch and switch to it.
|
54
|
+
Dir.chdir @root do
|
55
|
+
cmd = "git checkout -b \"#{newbranch}\""
|
56
|
+
stdout, stderr, status = Open3.capture3 cmd
|
57
|
+
if status != 0
|
58
|
+
case stderr
|
59
|
+
when /not a valid branch name/
|
60
|
+
raise InvalidParameter, "#{newbranch} is not a valid branch name"
|
61
|
+
when /already exists/
|
62
|
+
raise InvalidParameter, "#{newbranch} already exists"
|
63
|
+
when /Not a git repository/
|
64
|
+
raise NotARepositoryError
|
65
|
+
else
|
66
|
+
raise Error, stderr
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
# Next, save any working changes.
|
71
|
+
begin
|
72
|
+
Twit.save message
|
73
|
+
rescue NothingToCommitError
|
74
|
+
# New changes are not required for saveas.
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# Return an Array of branches in the repo.
|
79
|
+
def list
|
80
|
+
Dir.chdir @root do
|
81
|
+
cmd = "git branch"
|
82
|
+
stdout, stderr, status = Open3.capture3 cmd
|
83
|
+
if status != 0
|
84
|
+
case stderr
|
85
|
+
when /Not a git repository/
|
86
|
+
raise NotARepositoryError
|
87
|
+
else
|
88
|
+
raise Error, stderr
|
89
|
+
end
|
90
|
+
end
|
91
|
+
return stdout.split.map { |s|
|
92
|
+
# Remove trailing/leading whitespace and astericks
|
93
|
+
s.sub('*', '').strip
|
94
|
+
}.reject { |s|
|
95
|
+
# Drop elements created due to trailing newline
|
96
|
+
s.size == 0
|
97
|
+
}
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# Return the current branch.
|
102
|
+
def current_branch
|
103
|
+
Dir.chdir @root do
|
104
|
+
cmd = "git rev-parse --abbrev-ref HEAD"
|
105
|
+
stdout, stderr, status = Open3.capture3 cmd
|
106
|
+
if status != 0
|
107
|
+
case stderr
|
108
|
+
when /unknown revision/
|
109
|
+
raise Error, "could not determine branch of repo with no commits"
|
110
|
+
when /Not a git repository/
|
111
|
+
raise NotARepositoryError
|
112
|
+
else
|
113
|
+
raise Error, stderr
|
114
|
+
end
|
115
|
+
end
|
116
|
+
return stdout.strip
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# Open a branch.
|
121
|
+
def open branch
|
122
|
+
Dir.chdir @root do
|
123
|
+
cmd = "git checkout \"#{branch}\""
|
124
|
+
stdout, stderr, status = Open3.capture3 cmd
|
125
|
+
if status != 0
|
126
|
+
case stderr
|
127
|
+
when /Not a git repository/
|
128
|
+
raise NotARepositoryError
|
129
|
+
when /pathspec '#{branch}' did not match any/
|
130
|
+
raise InvalidParameter, "#{branch} does not exist"
|
131
|
+
else
|
132
|
+
raise Error, stderr
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
# Clean the working directory (permanently deletes changes!!!).
|
139
|
+
def discard
|
140
|
+
Dir.chdir @root do
|
141
|
+
# First, add all files to the index. (Otherwise, we won't discard new
|
142
|
+
# files.) Then, hard reset to revert to the last saved state.
|
143
|
+
cmd = "git add --all && git reset --hard"
|
144
|
+
stdout, stderr, status = Open3.capture3 cmd
|
145
|
+
if status != 0
|
146
|
+
case stderr
|
147
|
+
when /Not a git repository/
|
148
|
+
raise NotARepositoryError
|
149
|
+
else
|
150
|
+
raise Error, stderr
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
# Incorperate changes from another branch, but do not commit them.
|
157
|
+
#
|
158
|
+
# Return true if the merge was successful without conflicts; false if there
|
159
|
+
# are conflicts.
|
160
|
+
def include other_branch
|
161
|
+
Dir.chdir @root do
|
162
|
+
cmd = "git merge --no-ff --no-commit \"#{other_branch}\""
|
163
|
+
stdout, stderr, status = Open3.capture3 cmd
|
164
|
+
if status != 0
|
165
|
+
if /Not a git repository/.match stderr
|
166
|
+
raise NotARepositoryError
|
167
|
+
elsif /Automatic merge failed/.match stdout
|
168
|
+
return false
|
169
|
+
else
|
170
|
+
raise Error, stderr
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
return true
|
175
|
+
end
|
176
|
+
|
177
|
+
# Reverse of {Twit::Repo#include} -- incorperate changes from the current
|
178
|
+
# branch into another.
|
179
|
+
def include_into other_branch
|
180
|
+
original_branch = current_branch
|
181
|
+
open other_branch
|
182
|
+
include(original_branch)
|
183
|
+
end
|
184
|
+
|
185
|
+
# Return true if there is nothing new to commit.
|
186
|
+
def nothing_to_commit?
|
187
|
+
Dir.chdir @root do
|
188
|
+
cmd = "git status"
|
189
|
+
stdout, stderr, status = Open3.capture3 cmd
|
190
|
+
if status != 0
|
191
|
+
case stderr
|
192
|
+
when /Not a git repository/
|
193
|
+
raise NotARepositoryError
|
194
|
+
else
|
195
|
+
raise Error, stderr
|
196
|
+
end
|
197
|
+
end
|
198
|
+
# Check if status indicates nothing to commit
|
199
|
+
return /nothing to commit/.match stdout
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
33
203
|
end
|
34
204
|
|
35
205
|
end
|
data/lib/twit/version.rb
CHANGED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'tmpdir'
|
2
|
+
require 'twit'
|
3
|
+
|
4
|
+
shared_context "temp repo" do
|
5
|
+
before do
|
6
|
+
@tmpdir = Dir.mktmpdir
|
7
|
+
@repo = Twit.init @tmpdir
|
8
|
+
@oldwd = Dir.getwd
|
9
|
+
Dir.chdir @tmpdir
|
10
|
+
end
|
11
|
+
after do
|
12
|
+
Dir.chdir @oldwd
|
13
|
+
FileUtils.remove_entry @tmpdir
|
14
|
+
end
|
15
|
+
end
|
data/spec/twit/cli_spec.rb
CHANGED
@@ -11,28 +11,44 @@ describe Twit::CLI do
|
|
11
11
|
end
|
12
12
|
|
13
13
|
describe "init" do
|
14
|
-
|
15
|
-
|
16
|
-
|
14
|
+
context "is not repo" do
|
15
|
+
before do
|
16
|
+
Twit.stub(:'is_repo?') { false }
|
17
|
+
end
|
18
|
+
it "calls Twit.init" do
|
19
|
+
expect(Twit).to receive(:init)
|
20
|
+
@cli.invoke :init
|
21
|
+
end
|
17
22
|
end
|
18
23
|
end
|
19
24
|
|
20
25
|
describe "save" do
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
26
|
+
context "need to commit" do
|
27
|
+
before do
|
28
|
+
Twit.stub(:'nothing_to_commit?') { false }
|
29
|
+
end
|
30
|
+
it "calls Twit.save" do
|
31
|
+
message = "my test commit message"
|
32
|
+
expect(Twit).to receive(:save).with(message)
|
33
|
+
@cli.invoke :save, [message]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
28
37
|
|
29
|
-
|
38
|
+
describe "saveas" do
|
39
|
+
it "calls Twit.saveas" do
|
40
|
+
Twit.stub(:'nothing_to_commit?') { false }
|
41
|
+
branch = "my_branch"
|
42
|
+
message = "my test commit message"
|
43
|
+
expect(Twit).to receive(:saveas).with(branch, message)
|
44
|
+
@cli.invoke :saveas, [branch, message]
|
30
45
|
end
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
46
|
+
end
|
47
|
+
|
48
|
+
describe "discard" do
|
49
|
+
it "calls Twit.discard" do
|
50
|
+
expect(Twit).to receive(:discard)
|
51
|
+
@cli.invoke :discard
|
36
52
|
end
|
37
53
|
end
|
38
54
|
|
data/spec/twit/repo_spec.rb
CHANGED
@@ -1,10 +1,12 @@
|
|
1
1
|
require 'tmpdir'
|
2
2
|
require 'tempfile'
|
3
|
+
require 'securerandom'
|
3
4
|
|
4
|
-
require 'twit
|
5
|
-
require '
|
5
|
+
require 'twit'
|
6
|
+
require 'spec_helper'
|
6
7
|
|
7
8
|
describe Twit::Repo do
|
9
|
+
|
8
10
|
describe "#new" do
|
9
11
|
|
10
12
|
before do
|
@@ -58,4 +60,295 @@ describe Twit::Repo do
|
|
58
60
|
end
|
59
61
|
|
60
62
|
end
|
63
|
+
|
64
|
+
describe "#save" do
|
65
|
+
|
66
|
+
include_context "temp repo"
|
67
|
+
|
68
|
+
context "files in working tree" do
|
69
|
+
before do
|
70
|
+
3.times do |i|
|
71
|
+
File.open("file#{i}.txt", 'w') { |f| f.write("file#{i} contents\n") }
|
72
|
+
end
|
73
|
+
end
|
74
|
+
it "commits the entire working tree" do
|
75
|
+
@repo.save "created three files"
|
76
|
+
expect(`git status`).to include('working directory clean')
|
77
|
+
end
|
78
|
+
it "makes a commit" do
|
79
|
+
msg = "commit msg #{SecureRandom.hex(4)}"
|
80
|
+
@repo.save msg
|
81
|
+
expect(`git log`).to include(msg)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
it "raises error in new repository" do
|
86
|
+
expect {
|
87
|
+
@repo.save "trying to save"
|
88
|
+
}.to raise_error(Twit::NothingToCommitError)
|
89
|
+
end
|
90
|
+
|
91
|
+
it "raises error with nothing to commit" do
|
92
|
+
# First, make sure there's at least one commit on the log.
|
93
|
+
File.open("foo", 'w') { |f| f.write("bar\n") }
|
94
|
+
`git add foo && git commit -m "add foo"`
|
95
|
+
|
96
|
+
# Now there should be nothing more to commit
|
97
|
+
expect {
|
98
|
+
@repo.save "trying to save"
|
99
|
+
}.to raise_error(Twit::NothingToCommitError)
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
|
104
|
+
describe "#list" do
|
105
|
+
|
106
|
+
include_context "temp repo"
|
107
|
+
|
108
|
+
it "should return an Array of branches" do
|
109
|
+
# Make sure there's at least one commit on master.
|
110
|
+
File.open("foo", 'w') { |f| f.write("bar\n") }
|
111
|
+
|
112
|
+
@repo.save "Initial commit"
|
113
|
+
|
114
|
+
branches = ['all', 'your', 'branch', 'are',
|
115
|
+
'belong', 'to', 'us']
|
116
|
+
branches.each do |branch|
|
117
|
+
`git branch #{branch}`
|
118
|
+
end
|
119
|
+
|
120
|
+
expect(@repo.list).to match_array(branches + ['master'])
|
121
|
+
end
|
122
|
+
|
123
|
+
it "should return empty on an empty repo" do
|
124
|
+
expect(@repo.list).to match_array([])
|
125
|
+
end
|
126
|
+
|
127
|
+
end
|
128
|
+
|
129
|
+
describe "#discard" do
|
130
|
+
|
131
|
+
include_context "temp repo"
|
132
|
+
|
133
|
+
context "with one commit and dirty working tree" do
|
134
|
+
|
135
|
+
before do
|
136
|
+
# Make sure there's at least one commit on master.
|
137
|
+
File.open("foo", 'w') { |f| f.write("bar\n") }
|
138
|
+
@repo.save "Initial commit"
|
139
|
+
|
140
|
+
# Pollute the working tree with spam
|
141
|
+
File.open("spam", 'w') { |f| f.write("eggs\n") }
|
142
|
+
end
|
143
|
+
|
144
|
+
it "should clean the working tree" do
|
145
|
+
@repo.discard
|
146
|
+
expect(`git status`).to include('working directory clean')
|
147
|
+
end
|
148
|
+
|
149
|
+
it "should delete the spam file" do
|
150
|
+
@repo.discard
|
151
|
+
expect(Pathname "spam").not_to exist
|
152
|
+
end
|
153
|
+
|
154
|
+
end
|
155
|
+
|
156
|
+
end
|
157
|
+
|
158
|
+
describe "#saveas" do
|
159
|
+
|
160
|
+
include_context "temp repo"
|
161
|
+
|
162
|
+
context "files in working tree" do
|
163
|
+
before do
|
164
|
+
3.times do |i|
|
165
|
+
File.open("file#{i}.txt", 'w') { |f| f.write("file#{i} contents\n") }
|
166
|
+
end
|
167
|
+
end
|
168
|
+
it "commits the entire working tree" do
|
169
|
+
@repo.saveas "newbranch"
|
170
|
+
expect(`git status`).to include('working directory clean')
|
171
|
+
end
|
172
|
+
it "makes a commit" do
|
173
|
+
msg = "commit msg #{SecureRandom.hex(4)}"
|
174
|
+
@repo.saveas "newbranch", msg
|
175
|
+
expect(`git log`).to include(msg)
|
176
|
+
end
|
177
|
+
it "creates a new branch" do
|
178
|
+
new_branch = "branch-#{SecureRandom.hex(4)}"
|
179
|
+
@repo.saveas new_branch
|
180
|
+
current_branch = `git rev-parse --abbrev-ref HEAD`.strip
|
181
|
+
expect(current_branch).to eq(new_branch)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
it "raises an error with an invalid branch name" do
|
186
|
+
expect {
|
187
|
+
@repo.saveas "new branch"
|
188
|
+
}.to raise_error(Twit::InvalidParameter)
|
189
|
+
end
|
190
|
+
|
191
|
+
it "raises an error when the branch already exists" do
|
192
|
+
branch = "my_branch"
|
193
|
+
File.open("foo", 'w') { |f| f.write("bar\n") }
|
194
|
+
@repo.saveas branch
|
195
|
+
expect {
|
196
|
+
@repo.saveas branch
|
197
|
+
}.to raise_error(Twit::InvalidParameter)
|
198
|
+
end
|
199
|
+
|
200
|
+
it "does not raise error in new repository" do
|
201
|
+
expect {
|
202
|
+
@repo.saveas "newbranch"
|
203
|
+
}.not_to raise_error
|
204
|
+
end
|
205
|
+
|
206
|
+
it "does not raise error with nothing to commit" do
|
207
|
+
# First, make sure there's at least one commit on the log.
|
208
|
+
File.open("foo", 'w') { |f| f.write("bar\n") }
|
209
|
+
`git add foo && git commit -m "add foo"`
|
210
|
+
|
211
|
+
# Now there should be nothing more to commit, but no error should be
|
212
|
+
# raised.
|
213
|
+
expect {
|
214
|
+
@repo.saveas "newbranch"
|
215
|
+
}.not_to raise_error
|
216
|
+
end
|
217
|
+
|
218
|
+
end
|
219
|
+
|
220
|
+
describe "#current_branch" do
|
221
|
+
|
222
|
+
include_context "temp repo"
|
223
|
+
|
224
|
+
it "returns the name of the current branch" do
|
225
|
+
new_branch = "branch-#{SecureRandom.hex(4)}"
|
226
|
+
File.open("foo", 'w') { |f| f.write("bar\n") }
|
227
|
+
@repo.saveas new_branch
|
228
|
+
expect(@repo.current_branch).to eq(new_branch)
|
229
|
+
end
|
230
|
+
|
231
|
+
end
|
232
|
+
|
233
|
+
describe "#nothing_to_commit?" do
|
234
|
+
include_context "temp repo"
|
235
|
+
it "returns true in fresh repo" do
|
236
|
+
expect(@repo.nothing_to_commit?).to be_true
|
237
|
+
end
|
238
|
+
it "returns false with file in working tree" do
|
239
|
+
File.open("foo", 'w') { |f| f.write("bar\n") }
|
240
|
+
expect(@repo.nothing_to_commit?).to be_false
|
241
|
+
end
|
242
|
+
it "returns true after one commit" do
|
243
|
+
File.open("foo", 'w') { |f| f.write("bar\n") }
|
244
|
+
@repo.save "commit"
|
245
|
+
expect(@repo.nothing_to_commit?).to be_true
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
describe "#open" do
|
250
|
+
|
251
|
+
include_context "temp repo"
|
252
|
+
|
253
|
+
it "opens a branch" do
|
254
|
+
new_branch1 = "branch-#{SecureRandom.hex(4)}"
|
255
|
+
File.open("foo", 'w') { |f| f.write("bar\n") }
|
256
|
+
@repo.saveas new_branch1
|
257
|
+
|
258
|
+
new_branch2 = "branch-#{SecureRandom.hex(4)}"
|
259
|
+
@repo.saveas new_branch2
|
260
|
+
|
261
|
+
@repo.open new_branch1
|
262
|
+
current_branch = `git rev-parse --abbrev-ref HEAD`.strip
|
263
|
+
expect(current_branch).to eq(new_branch1)
|
264
|
+
end
|
265
|
+
|
266
|
+
it "raises error when branch does not exist" do
|
267
|
+
expect {
|
268
|
+
@repo.open "spam"
|
269
|
+
}.to raise_error(Twit::InvalidParameter)
|
270
|
+
end
|
271
|
+
|
272
|
+
end
|
273
|
+
|
274
|
+
describe "#include" do
|
275
|
+
|
276
|
+
include_context "temp repo"
|
277
|
+
|
278
|
+
shared_examples "simple merge" do
|
279
|
+
it "will create a merge commit" do
|
280
|
+
expect(`git status`).to include('All conflicts fixed but you are still merging')
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
context "fast-forward merge" do
|
285
|
+
before do
|
286
|
+
# First commit to master
|
287
|
+
File.open("foo", 'w') { |f| f.write("bar\n") }
|
288
|
+
@repo.save "add foo"
|
289
|
+
# Next commit to feature
|
290
|
+
File.open("spam", 'w') { |f| f.write("eggs\n") }
|
291
|
+
@repo.saveas "feature"
|
292
|
+
@repo.open "master"
|
293
|
+
@mergestatus = @repo.include "feature"
|
294
|
+
end
|
295
|
+
include_examples "simple merge"
|
296
|
+
end
|
297
|
+
|
298
|
+
context "no-conflict merge" do
|
299
|
+
before do
|
300
|
+
# First commit to master
|
301
|
+
File.open("foo", 'w') { |f| f.write("bar\n") }
|
302
|
+
@repo.save "add foo"
|
303
|
+
# Next commit to feature
|
304
|
+
File.open("spam", 'w') { |f| f.write("eggs\n") }
|
305
|
+
@repo.saveas "feature"
|
306
|
+
# Add something else to master
|
307
|
+
@repo.open "master"
|
308
|
+
File.open("else", 'w') { |f| f.write("continued\n") }
|
309
|
+
@repo.save "continue work"
|
310
|
+
@mergestatus = @repo.include "feature"
|
311
|
+
end
|
312
|
+
include_examples "simple merge"
|
313
|
+
end
|
314
|
+
|
315
|
+
context "conflict merge" do
|
316
|
+
before do
|
317
|
+
# First commit to master
|
318
|
+
File.open("foo", 'w') { |f| f.write("bar\n") }
|
319
|
+
@repo.save "add foo"
|
320
|
+
# Next commit to feature
|
321
|
+
File.open("spam", 'w') { |f| f.write("eggs\n") }
|
322
|
+
@repo.saveas "feature"
|
323
|
+
# Conflicting commit to master
|
324
|
+
@repo.open "master"
|
325
|
+
File.open("spam", 'w') { |f| f.write("spam\n") }
|
326
|
+
@repo.save "continue work"
|
327
|
+
@mergestatus = @repo.include "feature"
|
328
|
+
end
|
329
|
+
it "returns false for conflicts" do
|
330
|
+
expect(@mergestatus).to be_false
|
331
|
+
end
|
332
|
+
end
|
333
|
+
|
334
|
+
end
|
335
|
+
|
336
|
+
describe "#include_into" do
|
337
|
+
include_context "temp repo"
|
338
|
+
it "calls current_branch, open, and include" do
|
339
|
+
current_branch = "feature"
|
340
|
+
other_branch = "master"
|
341
|
+
|
342
|
+
@repo.stub(:current_branch) { current_branch }
|
343
|
+
@repo.stub(:open)
|
344
|
+
@repo.stub(:include) { |branch| true }
|
345
|
+
|
346
|
+
@repo.include_into other_branch
|
347
|
+
|
348
|
+
expect(@repo).to have_received(:current_branch)
|
349
|
+
expect(@repo).to have_received(:open).with(other_branch)
|
350
|
+
expect(@repo).to have_received(:include).with(current_branch)
|
351
|
+
end
|
352
|
+
end
|
353
|
+
|
61
354
|
end
|
data/spec/twit_spec.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
require "twit"
|
2
2
|
|
3
|
+
require "tmpdir"
|
4
|
+
|
3
5
|
describe Twit do
|
4
6
|
|
5
7
|
describe "::repo" do
|
@@ -9,4 +11,160 @@ describe Twit do
|
|
9
11
|
end
|
10
12
|
end
|
11
13
|
|
14
|
+
describe "::init" do
|
15
|
+
|
16
|
+
before do
|
17
|
+
@tmpdir = Dir.mktmpdir
|
18
|
+
end
|
19
|
+
|
20
|
+
after do
|
21
|
+
FileUtils.remove_entry @tmpdir
|
22
|
+
end
|
23
|
+
|
24
|
+
# Check if the current working directory is a git repository.
|
25
|
+
def expect_cwd_to_be_repo
|
26
|
+
`git status`
|
27
|
+
expect($?.exitstatus).to eq(0)
|
28
|
+
end
|
29
|
+
|
30
|
+
it "initializes given directory" do
|
31
|
+
Twit.init @tmpdir
|
32
|
+
Dir.chdir @tmpdir do
|
33
|
+
expect_cwd_to_be_repo
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
it "initializes the working directory by default" do
|
38
|
+
Dir.chdir @tmpdir do
|
39
|
+
Twit.init
|
40
|
+
expect_cwd_to_be_repo
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
it "returns a repo object" do
|
45
|
+
repo = Twit.init @tmpdir
|
46
|
+
expect(repo).to be_instance_of(Twit::Repo)
|
47
|
+
end
|
48
|
+
|
49
|
+
it "does not raise error on double-initialize" do
|
50
|
+
Twit.init @tmpdir
|
51
|
+
expect {
|
52
|
+
Twit.init @tmpdir
|
53
|
+
}.not_to raise_error
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
shared_context "stub repo" do
|
59
|
+
before do
|
60
|
+
@repo = double('repo')
|
61
|
+
Twit.stub(:repo) { @repo }
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe "::save" do
|
66
|
+
include_context "stub repo"
|
67
|
+
it "passes to default Repo object" do
|
68
|
+
message = "my test commit message"
|
69
|
+
expect(@repo).to receive(:save).with(message)
|
70
|
+
Twit.save message
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
describe "::saveas" do
|
75
|
+
include_context "stub repo"
|
76
|
+
it "passes to default Repo object" do
|
77
|
+
branch = "my_branch"
|
78
|
+
message = "my test commit message"
|
79
|
+
expect(@repo).to receive(:saveas).with(branch, message)
|
80
|
+
Twit.saveas branch, message
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
describe "::discard" do
|
85
|
+
include_context "stub repo"
|
86
|
+
it "passes to default Repo object" do
|
87
|
+
expect(@repo).to receive(:discard)
|
88
|
+
Twit.discard
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
describe "::list" do
|
93
|
+
include_context "stub repo"
|
94
|
+
it "passes to default Repo object" do
|
95
|
+
expect(@repo).to receive(:list)
|
96
|
+
Twit.list
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
describe "::open" do
|
101
|
+
include_context "stub repo"
|
102
|
+
it "passes to default Repo object" do
|
103
|
+
branch = "my_branch"
|
104
|
+
expect(@repo).to receive(:open).with(branch)
|
105
|
+
Twit.open branch
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
describe "::include" do
|
110
|
+
include_context "stub repo"
|
111
|
+
it "passes to default Repo object" do
|
112
|
+
branch = "my_branch"
|
113
|
+
expect(@repo).to receive(:include).with(branch)
|
114
|
+
Twit.include branch
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
describe "::include_into" do
|
119
|
+
include_context "stub repo"
|
120
|
+
it "passes to default Repo object" do
|
121
|
+
branch = "my_branch"
|
122
|
+
expect(@repo).to receive(:include_into).with(branch)
|
123
|
+
Twit.include_into branch
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
describe "::current_branch" do
|
128
|
+
include_context "stub repo"
|
129
|
+
it "passes to default Repo object" do
|
130
|
+
expect(@repo).to receive(:current_branch)
|
131
|
+
Twit.current_branch
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
describe "::nothing_to_commit?" do
|
136
|
+
include_context "stub repo"
|
137
|
+
it "passes to default Repo object" do
|
138
|
+
expect(@repo).to receive(:'nothing_to_commit?')
|
139
|
+
Twit.nothing_to_commit?
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
describe "::is_repo?" do
|
144
|
+
context "with no repo" do
|
145
|
+
before do
|
146
|
+
@tmpdir = Dir.mktmpdir
|
147
|
+
end
|
148
|
+
after do
|
149
|
+
FileUtils.remove_entry @tmpdir
|
150
|
+
end
|
151
|
+
it "returns false on empty directory" do
|
152
|
+
expect(Twit.is_repo? @tmpdir).to be_false
|
153
|
+
end
|
154
|
+
it "detects the current directory" do
|
155
|
+
Dir.chdir @tmpdir do
|
156
|
+
expect(Twit.is_repo?).to be_false
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
context "with a real repo" do
|
161
|
+
it "takes a directory as argument" do
|
162
|
+
expect(Twit.is_repo? @tmpdir).to be_true
|
163
|
+
end
|
164
|
+
it "detects the current directory" do
|
165
|
+
expect(Twit.is_repo?).to be_true
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
12
170
|
end
|
data/twit.gemspec
CHANGED
@@ -8,8 +8,8 @@ Gem::Specification.new do |spec|
|
|
8
8
|
spec.version = Twit::VERSION
|
9
9
|
spec.authors = ["Ben Pringle"]
|
10
10
|
spec.email = ["ben.pringle@gmail.com"]
|
11
|
-
spec.description = %q{Create a simpler abstraction over the
|
12
|
-
spec.summary = %q{
|
11
|
+
spec.description = %q{Create a simpler abstraction over the Git command}
|
12
|
+
spec.summary = %q{Training wheels for Git}
|
13
13
|
spec.homepage = "https://github.com/Pringley/twit"
|
14
14
|
spec.license = "MIT"
|
15
15
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: twit
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ben Pringle
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-08-
|
11
|
+
date: 2013-08-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|
@@ -80,7 +80,7 @@ dependencies:
|
|
80
80
|
- - '>='
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '0'
|
83
|
-
description: Create a simpler abstraction over the
|
83
|
+
description: Create a simpler abstraction over the Git command
|
84
84
|
email:
|
85
85
|
- ben.pringle@gmail.com
|
86
86
|
executables:
|
@@ -99,13 +99,10 @@ files:
|
|
99
99
|
- lib/twit.rb
|
100
100
|
- lib/twit/cli.rb
|
101
101
|
- lib/twit/error.rb
|
102
|
-
- lib/twit/init.rb
|
103
102
|
- lib/twit/repo.rb
|
104
|
-
- lib/twit/repo/save.rb
|
105
103
|
- lib/twit/version.rb
|
104
|
+
- spec/spec_helper.rb
|
106
105
|
- spec/twit/cli_spec.rb
|
107
|
-
- spec/twit/init_spec.rb
|
108
|
-
- spec/twit/repo/save_spec.rb
|
109
106
|
- spec/twit/repo_spec.rb
|
110
107
|
- spec/twit_spec.rb
|
111
108
|
- twit.gemspec
|
@@ -132,11 +129,10 @@ rubyforge_project:
|
|
132
129
|
rubygems_version: 2.0.2
|
133
130
|
signing_key:
|
134
131
|
specification_version: 4
|
135
|
-
summary:
|
132
|
+
summary: Training wheels for Git
|
136
133
|
test_files:
|
134
|
+
- spec/spec_helper.rb
|
137
135
|
- spec/twit/cli_spec.rb
|
138
|
-
- spec/twit/init_spec.rb
|
139
|
-
- spec/twit/repo/save_spec.rb
|
140
136
|
- spec/twit/repo_spec.rb
|
141
137
|
- spec/twit_spec.rb
|
142
138
|
has_rdoc:
|
data/lib/twit/init.rb
DELETED
@@ -1,21 +0,0 @@
|
|
1
|
-
require 'open3'
|
2
|
-
require 'twit/error'
|
3
|
-
|
4
|
-
module Twit
|
5
|
-
|
6
|
-
# Initialize a git repository in a directory. Return a Twit::Repo object
|
7
|
-
# representing the new repository.
|
8
|
-
#
|
9
|
-
# If no argument is supplied, use the working directory.
|
10
|
-
def self.init dir = nil
|
11
|
-
dir ||= Dir.getwd
|
12
|
-
Dir.chdir dir do
|
13
|
-
stdout, stderr, status = Open3.capture3 "git init"
|
14
|
-
if status != 0
|
15
|
-
raise Error, stderr
|
16
|
-
end
|
17
|
-
end
|
18
|
-
Repo.new dir
|
19
|
-
end
|
20
|
-
|
21
|
-
end
|
data/lib/twit/repo/save.rb
DELETED
@@ -1,23 +0,0 @@
|
|
1
|
-
require 'open3'
|
2
|
-
require 'twit/error'
|
3
|
-
|
4
|
-
module Twit
|
5
|
-
class Repo
|
6
|
-
|
7
|
-
# Update the snapshot of the current directory.
|
8
|
-
def save message
|
9
|
-
Dir.chdir @root do
|
10
|
-
cmd = "git add --all && git commit -m \"#{message}\""
|
11
|
-
stdout, stderr, status = Open3.capture3 cmd
|
12
|
-
if status != 0
|
13
|
-
if /nothing to commit/.match stdout
|
14
|
-
raise NothingToCommitError
|
15
|
-
else
|
16
|
-
raise Error, stderr
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
end
|
23
|
-
end
|
data/spec/twit/init_spec.rb
DELETED
@@ -1,43 +0,0 @@
|
|
1
|
-
require 'tmpdir'
|
2
|
-
|
3
|
-
require 'twit/init'
|
4
|
-
require 'twit/repo'
|
5
|
-
|
6
|
-
describe Twit do
|
7
|
-
describe "::init" do
|
8
|
-
|
9
|
-
before do
|
10
|
-
@tmpdir = Dir.mktmpdir
|
11
|
-
end
|
12
|
-
|
13
|
-
after do
|
14
|
-
FileUtils.remove_entry @tmpdir
|
15
|
-
end
|
16
|
-
|
17
|
-
# Check if the current working directory is a git repository.
|
18
|
-
def expect_cwd_to_be_repo
|
19
|
-
`git status`
|
20
|
-
expect($?.exitstatus).to eq(0)
|
21
|
-
end
|
22
|
-
|
23
|
-
it "initializes given directory" do
|
24
|
-
Twit.init @tmpdir
|
25
|
-
Dir.chdir @tmpdir do
|
26
|
-
expect_cwd_to_be_repo
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
it "initializes the working directory by default" do
|
31
|
-
Dir.chdir @tmpdir do
|
32
|
-
Twit.init
|
33
|
-
expect_cwd_to_be_repo
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
it "returns a repo object" do
|
38
|
-
repo = Twit.init @tmpdir
|
39
|
-
expect(repo).to be_instance_of(Twit::Repo)
|
40
|
-
end
|
41
|
-
|
42
|
-
end
|
43
|
-
end
|
data/spec/twit/repo/save_spec.rb
DELETED
@@ -1,55 +0,0 @@
|
|
1
|
-
require 'tmpdir'
|
2
|
-
require 'securerandom'
|
3
|
-
|
4
|
-
require 'twit/repo'
|
5
|
-
require 'twit/error'
|
6
|
-
|
7
|
-
describe Twit::Repo, "#save" do
|
8
|
-
|
9
|
-
before do
|
10
|
-
@tmpdir = Dir.mktmpdir
|
11
|
-
@repo = Twit.init @tmpdir
|
12
|
-
@oldwd = Dir.getwd
|
13
|
-
Dir.chdir @tmpdir
|
14
|
-
end
|
15
|
-
|
16
|
-
after do
|
17
|
-
Dir.chdir @oldwd
|
18
|
-
FileUtils.remove_entry @tmpdir
|
19
|
-
end
|
20
|
-
|
21
|
-
context "files in working tree" do
|
22
|
-
before do
|
23
|
-
3.times do |i|
|
24
|
-
File.open("file#{i}.txt", 'w') { |f| f.write("file#{i} contents\n") }
|
25
|
-
end
|
26
|
-
end
|
27
|
-
it "commits the entire working tree" do
|
28
|
-
@repo.save "created three files"
|
29
|
-
expect(`git status`).to include('working directory clean')
|
30
|
-
end
|
31
|
-
it "makes a commit" do
|
32
|
-
msg = "commit msg #{SecureRandom.hex(4)}"
|
33
|
-
@repo.save msg
|
34
|
-
expect(`git log`).to include(msg)
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
it "raises error in new repository" do
|
39
|
-
expect {
|
40
|
-
@repo.save "trying to save"
|
41
|
-
}.to raise_error(Twit::NothingToCommitError)
|
42
|
-
end
|
43
|
-
|
44
|
-
it "raises error with nothing to commit" do
|
45
|
-
# First, make sure there's at least one commit on the log.
|
46
|
-
File.open("foo", 'w') { |f| f.write("bar\n") }
|
47
|
-
`git add foo && git commit -m "add foo"`
|
48
|
-
|
49
|
-
# Now there should be nothing more to commit
|
50
|
-
expect {
|
51
|
-
@repo.save "trying to save"
|
52
|
-
}.to raise_error(Twit::NothingToCommitError)
|
53
|
-
end
|
54
|
-
|
55
|
-
end
|