scvcs 0.2.0
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 +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
|
+

|
@@ -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
|