scvcs 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +20 -0
- data/README.md +152 -0
- data/bin/commands/branch.rb +86 -0
- data/bin/commands/commit.rb +88 -0
- data/bin/commands/config.rb +35 -0
- data/bin/commands/diff.rb +19 -0
- data/bin/commands/history.rb +19 -0
- data/bin/commands/init.rb +17 -0
- data/bin/commands/merge.rb +56 -0
- data/bin/commands/remote.rb +157 -0
- data/bin/commands/restore.rb +17 -0
- data/bin/commands/server.rb +10 -0
- data/bin/commands/status.rb +52 -0
- data/bin/formatters/changeset.rb +43 -0
- data/bin/formatters/hierarchy.rb +43 -0
- data/bin/formatters/merge_report.rb +25 -0
- data/bin/scv +107 -0
- data/bin/utilities/output.rb +38 -0
- data/bin/utilities/shell.rb +15 -0
- data/lib/scv.rb +13 -0
- data/lib/scv/config.rb +52 -0
- data/lib/scv/file_store.rb +81 -0
- data/lib/scv/http_file_store.rb +69 -0
- data/lib/scv/object_store.rb +125 -0
- data/lib/scv/objects.rb +4 -0
- data/lib/scv/objects/blob.rb +10 -0
- data/lib/scv/objects/commit.rb +34 -0
- data/lib/scv/objects/label.rb +10 -0
- data/lib/scv/objects/tree.rb +10 -0
- data/lib/scv/repository.rb +124 -0
- data/lib/scv/server.rb +66 -0
- data/lib/scv/version.rb +3 -0
- metadata +218 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: eefc4bffd875b5d3790a245d07af5d33c806bdd1
|
4
|
+
data.tar.gz: bc962cb8cbd75e9315f5ca9cbad99a0faaee8a45
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3fa4eb7036475bd632ff4e01928fd34815e86fa534d12e27ab28a1f1aaf19e36e1c5da461f15dd6444994a117e6dd068715ec98267c91a57f18339e13b289688
|
7
|
+
data.tar.gz: 02e00f9e40d47ac26016c98f079aac6a6c67fd09e00bd336576e16fc660603bbe4d0c49092dcac1b1f12dfba8b2c45ad63a9e4373615bbb3aa6ca6938939732b
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2013 Georgy Angelov
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
6
|
+
this software and associated documentation files (the "Software"), to deal in
|
7
|
+
the Software without restriction, including without limitation the rights to
|
8
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
9
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
10
|
+
subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
17
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
18
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
19
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
20
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,152 @@
|
|
1
|
+
SCV
|
2
|
+
===
|
3
|
+
The SCV is a tool that every terran player absolutely must use to have a stable and well-organized (code)base.
|
4
|
+
|
5
|
+
Seriously though...
|
6
|
+
===================
|
7
|
+
This project was inspired by [the Git lecture](http://fmi.ruby.bg/lectures/15-git#1) I made for [this year's Ruby course](http://fmi.ruby.bg) at my university. It is also my course project.
|
8
|
+
|
9
|
+
The name SCV has two different meanings:
|
10
|
+
|
11
|
+
1. It is `VCS` (Version Control System) in reverse
|
12
|
+
2. It is the name of a StarCraft unit (a worker) which can gather resources and can also build and repair stuff.
|
13
|
+
|
14
|
+
Overview
|
15
|
+
========
|
16
|
+
The goal is to create a working (you don't say) version control system in Ruby.
|
17
|
+
|
18
|
+
The project itself is split into two parts: [**VCSToolkit**](https://github.com/stormbreakerbg/vcs-toolkit) and **SCV**.
|
19
|
+
**VCSToolkit** is a Ruby gem that is supposed to provide the platform and common tools, upon which a VCS tool can be built. This is where most of the common operations for such a tool are (being) implemented. **SCV** only implements some of **VCSToolkit**'s interfaces, extends (minimally) its classes and provides a user-friendly command-line interface to its methods.
|
20
|
+
|
21
|
+
Features
|
22
|
+
========
|
23
|
+
*Note: The examples below use the form `scv <command>`, but if you want to test it right now, follow the 3 steps in the **Try it!** section and use `./run_scv <command>`.*
|
24
|
+
|
25
|
+
Currently implemented features:
|
26
|
+
|
27
|
+
---
|
28
|
+
`scv init`
|
29
|
+
|
30
|
+
Initializes an empty repository.
|
31
|
+
|
32
|
+
Actually creates a `.scv` directory in the current folder and the default `head` label (pointer to a `nil` commit).
|
33
|
+
|
34
|
+
---
|
35
|
+
`scv status`
|
36
|
+
|
37
|
+
Shows the list of created, modified and deleted files since the last commit.
|
38
|
+
|
39
|
+
---
|
40
|
+
`scv diff`
|
41
|
+
|
42
|
+
Shows the actual differences between the files in the last commit and the working directory.
|
43
|
+
|
44
|
+
---
|
45
|
+
`scv commit`
|
46
|
+
|
47
|
+
Commits the current state of the working directory. All changed, new and deleted files are commited.
|
48
|
+
You can explicitly set the commit author on every commit using the `--author` option or set it once with `scv config author "Your Name <your@email.com>"`.
|
49
|
+
|
50
|
+
You can also set the date (`--date`), set the commit message (`-m` or `--message`) or amend the last commit (`--amend`). If the `-m` flag is not set the default terminal editor will be opened (as in Git).
|
51
|
+
|
52
|
+
Example: `scv commit --author "Georgy Angelov <test@test.test>"`
|
53
|
+
|
54
|
+
---
|
55
|
+
`scv history` or `scv log`
|
56
|
+
|
57
|
+
Lists all commits, in reverse-chronological order with their **id**, **date**, **author** and **message**.
|
58
|
+
|
59
|
+
---
|
60
|
+
`scv restore <paths>...`
|
61
|
+
|
62
|
+
Restores files to the state they were in the `head` commit. `paths` can be files or directories. New files will not be removed, only changed and deleted files are restored.
|
63
|
+
|
64
|
+
You can optionally specify the source commit with `-s` or `--source` by giving its object_id, a label name that references it or an object_id and a relative offset (for example `head~3`).
|
65
|
+
|
66
|
+
---
|
67
|
+
`scv branch new <branch_name>` or `scv branch create <branch_name>`
|
68
|
+
|
69
|
+
Creates a new branch based on the current branch head.
|
70
|
+
|
71
|
+
---
|
72
|
+
`scv branch delete <branch_name>` or `scv branch remove <branch_name>`
|
73
|
+
|
74
|
+
Deletes the specified branch. The commits are not lost, only the label is deleted.
|
75
|
+
|
76
|
+
---
|
77
|
+
`scv branch switch <branch_name>`
|
78
|
+
|
79
|
+
Switches the current directory to the specified branch head. It works as follows:
|
80
|
+
- Detects the changes that should be made to switch from the current branch to the other
|
81
|
+
- If you have modified files that would have to be overrwriten (modified) fails with an error
|
82
|
+
- Keeps all of your new or modified files and only overwrites unmodified ones
|
83
|
+
- May restore any deleted files that are present in the other branch
|
84
|
+
- Switches the current branch to `branch_name` (the following commits will be on branch `branch_name`)
|
85
|
+
|
86
|
+
---
|
87
|
+
`scv config`
|
88
|
+
|
89
|
+
Lists all configuration properties and values. The output is similar to the output of the `tree` tool, because the configuration options can be nested:
|
90
|
+
|
91
|
+
level_0
|
92
|
+
├─ level_1_one
|
93
|
+
│ ├─ level_2_one: value
|
94
|
+
│ └─ level_2_two: true
|
95
|
+
└─ level_1_two: value
|
96
|
+
|
97
|
+
This configuration is stored in `.scv/config.yml` and is relative only to the current scv repository.
|
98
|
+
|
99
|
+
---
|
100
|
+
`scv config <key>`
|
101
|
+
|
102
|
+
Shows the current value of `<key>`. A key of the form `one.two.three` can be used to reference nested properties.
|
103
|
+
|
104
|
+
---
|
105
|
+
`scv config <key> <value>`
|
106
|
+
|
107
|
+
Sets the option `key` to `value`. As in the previous command, you can use the `one.two.three` form to reference nested properties. If the key doesn't exist it is created.
|
108
|
+
|
109
|
+
|
110
|
+
Try it!
|
111
|
+
=======
|
112
|
+
Since there are a lot of features currently missing, SCV is not yet available on RubyGems.
|
113
|
+
|
114
|
+
1. Clone this repository
|
115
|
+
2. `bundle install`
|
116
|
+
3. `./run_scv help`
|
117
|
+
|
118
|
+
.scv structure
|
119
|
+
================
|
120
|
+
|
121
|
+
.scv/
|
122
|
+
|
123
|
+
objects/
|
124
|
+
59/
|
125
|
+
59873e99cef61a60b3826e1cbb9d4b089ae78c2b.json
|
126
|
+
...
|
127
|
+
...
|
128
|
+
|
129
|
+
refs/
|
130
|
+
HEAD.json
|
131
|
+
master.json
|
132
|
+
...
|
133
|
+
|
134
|
+
blobs/
|
135
|
+
59/
|
136
|
+
59873e99cef61a60b3826e1cbb9d4b089ae78c2b
|
137
|
+
...
|
138
|
+
...
|
139
|
+
|
140
|
+
Each object in `.scv/objects/` is stored in a directory with a name of the first two symbols of the object id.
|
141
|
+
|
142
|
+
The blob objects follow the same naming scheme as the regular ones, but they are just a copy of the original user files (not in `json` format).
|
143
|
+
|
144
|
+
The refs are named objects (object.named? == true) and can be enumerated. Currently the only named objects are labels which are used as pointers to unnamed ones.
|
145
|
+
|
146
|
+
Other
|
147
|
+
=====
|
148
|
+
Contributions are most welcome, but I doubt there will be any :)
|
149
|
+
|
150
|
+
If you are interested in learning more about this you can ask me on Twitter [@stormbreakerbg](https://twitter.com/stormbreakerbg).
|
151
|
+
|
152
|
+
![SCV](http://static3.wikia.nocookie.net/__cb20080906211455/starcraft/images/2/24/SCV_SC2_Cncpt1.jpg)
|
@@ -0,0 +1,86 @@
|
|
1
|
+
desc 'A set of commands to manage branches.'
|
2
|
+
arg_name ''
|
3
|
+
command :branch do |c|
|
4
|
+
|
5
|
+
c.desc 'Create a branch'
|
6
|
+
c.arg_name '<branch name>'
|
7
|
+
c.command [:new, :create] do |create|
|
8
|
+
create.desc 'Specifies the commit/label that will be used as the branch head'
|
9
|
+
create.arg_name 'head'
|
10
|
+
create.default_value 'head'
|
11
|
+
create.flag [:b, :head]
|
12
|
+
|
13
|
+
create.action do |global_options, options, args|
|
14
|
+
branch_name = args.first
|
15
|
+
branch_head = options[:head]
|
16
|
+
|
17
|
+
raise 'No branch name specified' if branch_name.nil?
|
18
|
+
|
19
|
+
repository = global_options[:repository]
|
20
|
+
|
21
|
+
raise 'There is already a label with this name' if repository[branch_name]
|
22
|
+
|
23
|
+
repository.set_label branch_name, repository[branch_head, :commit].id
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
c.desc 'Delete a branch'
|
28
|
+
c.arg_name '<branch name>'
|
29
|
+
c.command [:delete, :remove] do |remove|
|
30
|
+
remove.action do |global_options, options, args|
|
31
|
+
branch_name = args.first
|
32
|
+
|
33
|
+
raise 'No branch name specified' if branch_name.nil?
|
34
|
+
|
35
|
+
repository = global_options[:repository]
|
36
|
+
branch = repository[branch_name]
|
37
|
+
|
38
|
+
raise 'The specified branch does not exits' if branch.nil?
|
39
|
+
raise 'The specified name is not a branch/label' if branch.object_type != :label
|
40
|
+
|
41
|
+
repository.delete_label branch_name
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
c.desc 'Switch head to another branch. Does not change any files'
|
46
|
+
c.arg_name '<branch name>'
|
47
|
+
c.command :switch do |switch|
|
48
|
+
switch.action do |global_options, options, args|
|
49
|
+
branch_name = args.first
|
50
|
+
|
51
|
+
raise 'No branch name specified' if branch_name.nil?
|
52
|
+
|
53
|
+
repository = global_options[:repository]
|
54
|
+
branch = repository[branch_name]
|
55
|
+
|
56
|
+
raise 'This is the current branch already' if repository.head == branch_name
|
57
|
+
raise 'There is no branch with this name' if branch.nil? or branch.object_type != :label
|
58
|
+
|
59
|
+
changes = repository.commit_status repository['head', :commit],
|
60
|
+
repository[branch_name, :commit]
|
61
|
+
|
62
|
+
status = repository.status repository['head', :commit],
|
63
|
+
ignore: [/^\.|\/\./]
|
64
|
+
|
65
|
+
# Check for conflicts
|
66
|
+
branch_changes = changes[:created] | changes[:changed] | changes[:deleted]
|
67
|
+
working_dir_changes = status[:created] | status[:changed] | status[:deleted]
|
68
|
+
conflicts = branch_changes & working_dir_changes
|
69
|
+
|
70
|
+
unless conflicts.empty?
|
71
|
+
raise 'Branch switch aborted due to conflics. Please commit or reset local changes'
|
72
|
+
end
|
73
|
+
|
74
|
+
repository.set_label :head, branch_name
|
75
|
+
|
76
|
+
# Reset unchanged files to their state in the new branch
|
77
|
+
commit = repository['head', :commit]
|
78
|
+
tree = repository[commit.tree]
|
79
|
+
|
80
|
+
tree.all_files(repository.object_store, ignore: working_dir_changes).each do |file, _|
|
81
|
+
repository.restore file, commit
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
desc 'Commits the current state of the working directory.'
|
2
|
+
arg_name ''
|
3
|
+
command :commit do |c|
|
4
|
+
c.desc 'The commit message'
|
5
|
+
c.arg_name 'message'
|
6
|
+
c.flag [:m, :message]
|
7
|
+
|
8
|
+
c.desc 'The commit author name and email.'
|
9
|
+
c.arg_name 'author'
|
10
|
+
c.flag :author
|
11
|
+
|
12
|
+
c.desc 'Use this to override the timestamp of the commit.'
|
13
|
+
c.arg_name 'date'
|
14
|
+
c.default_value DateTime.now
|
15
|
+
c.flag :date
|
16
|
+
|
17
|
+
c.desc 'Use this to replace the last commit'
|
18
|
+
c.arg_name 'amend'
|
19
|
+
c.switch :amend
|
20
|
+
|
21
|
+
c.action do |global_options, options, args|
|
22
|
+
repository = global_options[:repository]
|
23
|
+
repository_path = "#{global_options[:dir]}/.scv"
|
24
|
+
|
25
|
+
unless options[:amend]
|
26
|
+
status = repository.status repository[:head, :commit], ignore: [/^\.|\/\./]
|
27
|
+
|
28
|
+
if status.none? { |_, files| files.any? }
|
29
|
+
raise 'No changes since last commit'
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
if options[:author]
|
34
|
+
author = options[:author]
|
35
|
+
elsif repository.config['author']
|
36
|
+
author = repository.config['author']
|
37
|
+
else
|
38
|
+
raise 'Please provide an author with --author="..." or set it globally with `scv config author ...`'
|
39
|
+
end
|
40
|
+
|
41
|
+
if options[:amend] and repository.head.nil?
|
42
|
+
raise 'Amend requested but there are no commits'
|
43
|
+
end
|
44
|
+
|
45
|
+
if options[:message]
|
46
|
+
commit_message = options[:message].strip
|
47
|
+
else
|
48
|
+
commit_message_file = "#{repository_path}/COMMIT_MESSAGE"
|
49
|
+
|
50
|
+
if options[:amend]
|
51
|
+
File.write commit_message_file, repository[:head, :commit].message
|
52
|
+
end
|
53
|
+
|
54
|
+
system "$EDITOR #{commit_message_file}"
|
55
|
+
File.open(commit_message_file, 'r') do |file|
|
56
|
+
commit_message = file.read.strip
|
57
|
+
end
|
58
|
+
FileUtils.rm commit_message_file
|
59
|
+
end
|
60
|
+
|
61
|
+
if commit_message.empty?
|
62
|
+
raise 'The commit message cannot be empty'
|
63
|
+
end
|
64
|
+
|
65
|
+
date = options[:date].is_a?(String) ? DateTime.parse(options[:date]) : options[:date]
|
66
|
+
|
67
|
+
parents = nil
|
68
|
+
|
69
|
+
if options[:amend]
|
70
|
+
parents = repository[:head, :commit].parents
|
71
|
+
elsif repository.config['merge'] and repository.config['merge']['parents']
|
72
|
+
# There is a merge waiting for a commit creation
|
73
|
+
parents = repository.config['merge']['parents']
|
74
|
+
|
75
|
+
repository.config['merge'] = {}
|
76
|
+
end
|
77
|
+
|
78
|
+
repository.commit commit_message,
|
79
|
+
author,
|
80
|
+
date,
|
81
|
+
parents: parents,
|
82
|
+
ignore: [/^\.|\/\./]
|
83
|
+
|
84
|
+
if parents
|
85
|
+
repository.config.save
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
desc 'Sets/changes config parameters for this repository'
|
2
|
+
arg_name '[<option> [<value>]]'
|
3
|
+
command :config do |c|
|
4
|
+
c.desc 'Delete the specified key'
|
5
|
+
c.arg_name 'delete'
|
6
|
+
c.switch [:d, :delete]
|
7
|
+
|
8
|
+
c.action do |global_options, options, args|
|
9
|
+
repository = global_options[:repository]
|
10
|
+
|
11
|
+
if args.empty?
|
12
|
+
# List all config options in a `tree`-like hierarchy
|
13
|
+
output { SCV::Formatters::Hierarchy.print(repository.config.data) }
|
14
|
+
elsif args.size == 1
|
15
|
+
if options[:delete]
|
16
|
+
repository.config.delete args.first
|
17
|
+
repository.config.save
|
18
|
+
else
|
19
|
+
# Print the specified option value
|
20
|
+
value = repository.config[args.first]
|
21
|
+
|
22
|
+
if value.is_a? Hash
|
23
|
+
SCV::Formatters::Hierarchy.print(value)
|
24
|
+
elsif not value.nil?
|
25
|
+
puts value
|
26
|
+
end
|
27
|
+
end
|
28
|
+
else
|
29
|
+
# Set the specified option
|
30
|
+
repository.config[args.first] = args[1..args.size].join(' ')
|
31
|
+
repository.config.save
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
desc 'Displays the file changes in the working directory'
|
2
|
+
arg_name ''
|
3
|
+
command :diff do |c|
|
4
|
+
c.action do |global_options, options, args|
|
5
|
+
repository = global_options[:repository]
|
6
|
+
commit = repository[:head, :commit]
|
7
|
+
status = repository.status commit,
|
8
|
+
ignore: [/^\.|\/\./]
|
9
|
+
|
10
|
+
output do
|
11
|
+
status.each do |_, file_list|
|
12
|
+
file_list.each do |file_path|
|
13
|
+
changeset = repository.file_difference file_path, commit
|
14
|
+
SCV::Formatters::Changeset.print file_path, changeset
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
desc 'Displays the commit history'
|
2
|
+
arg_name ''
|
3
|
+
command [:history, :log] do |c|
|
4
|
+
c.action do |global_options, options, args|
|
5
|
+
repository = global_options[:repository]
|
6
|
+
|
7
|
+
output do
|
8
|
+
repository.history.each do |commit|
|
9
|
+
puts "Commit #{commit.id.yellow}"
|
10
|
+
puts "Author #{commit.author.blue}"
|
11
|
+
puts "Date #{commit.date.strftime "%A %Y-%m-%d %H:%M:%S %z".green}"
|
12
|
+
|
13
|
+
puts
|
14
|
+
puts commit.message.lines.map { |line| " #{line}" }.join ''
|
15
|
+
puts
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
desc 'Initialize an empty repository.'
|
2
|
+
command :init do |c|
|
3
|
+
|
4
|
+
c.action do |global_options, options, args|
|
5
|
+
path = global_options[:dir]
|
6
|
+
|
7
|
+
if File.directory? File.join(path, '.scv')
|
8
|
+
raise "There is already an SCV repository #{path == '.' ? 'here' : 'at ' + path}"
|
9
|
+
end
|
10
|
+
|
11
|
+
SCV::Repository.create_at path
|
12
|
+
|
13
|
+
puts "Job's finished!"
|
14
|
+
puts "You can now use `scv commit` to create your first commit!"
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|