git-si 0.3.1 → 0.4.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 +4 -4
- data/git-si.gemspec +2 -1
- data/lib/git/si.rb +68 -369
- data/lib/git/si/actions.rb +153 -0
- data/lib/git/si/errors.rb +20 -0
- data/lib/git/si/git-control.rb +112 -0
- data/lib/git/si/git-ignore.rb +23 -0
- data/lib/git/si/svn-control.rb +135 -0
- data/lib/git/si/util.rb +304 -0
- data/lib/git/si/version.rb +12 -1
- data/spec/actions_spec.rb +356 -0
- data/spec/git-control_spec.rb +254 -0
- data/spec/git-ignore_spec.rb +37 -0
- data/spec/svn-control_spec.rb +391 -0
- data/spec/util_spec.rb +458 -0
- data/spec/version_spec.rb +17 -0
- metadata +40 -8
@@ -0,0 +1,153 @@
|
|
1
|
+
require "git/si/errors"
|
2
|
+
|
3
|
+
module Git
|
4
|
+
|
5
|
+
module Si
|
6
|
+
|
7
|
+
module Actions
|
8
|
+
|
9
|
+
def do_status_action( args=[] )
|
10
|
+
on_local_branch do
|
11
|
+
return if do_revisions_differ()
|
12
|
+
svn_status = get_command_output( Git::Si::SvnControl.status_command( args ) )
|
13
|
+
print_colordiff Git::Si::SvnControl.parse_svn_status( svn_status ).join( "\n" )
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def do_diff_action( args=[] )
|
18
|
+
on_local_branch do
|
19
|
+
return if do_revisions_differ()
|
20
|
+
|
21
|
+
notice_message "Adding any files that are not already in svn to ensure an accurate diff."
|
22
|
+
do_readd_action
|
23
|
+
|
24
|
+
print_colordiff get_command_output( Git::Si::SvnControl.diff_command( args ) )
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def do_add_action( args=[] )
|
29
|
+
on_local_branch do
|
30
|
+
run_command( Git::Si::SvnControl.add_command( args ) )
|
31
|
+
run_command( Git::Si::GitControl.add_command( args ) )
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def do_readd_action
|
36
|
+
on_local_branch do
|
37
|
+
files_to_add = []
|
38
|
+
Git::Si::SvnControl.parse_unknown_files( get_command_output( Git::Si::SvnControl.status_command ) ).each do |filename|
|
39
|
+
if is_file_in_git?( filename )
|
40
|
+
files_to_add << filename if filename != '.gitignore'
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
if files_to_add.empty?
|
45
|
+
notice_message "There are no files to add."
|
46
|
+
return
|
47
|
+
end
|
48
|
+
|
49
|
+
using_stderr do
|
50
|
+
files_to_add.each do |filename|
|
51
|
+
say filename
|
52
|
+
end
|
53
|
+
if yes? "Do you want to add the above files to svn? [y/N] ", :green
|
54
|
+
run_command( Git::Si::SvnControl.add_command( files_to_add ) )
|
55
|
+
success_message "Added files to svn that had been added to git."
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def do_fetch_action
|
63
|
+
stashed_changes = stash_local_changes
|
64
|
+
on_mirror_branch do
|
65
|
+
notice_message "Fetching remote data from svn"
|
66
|
+
updated_files = get_command_output( Git::Si::SvnControl.update_command )
|
67
|
+
revert_files_to_svn_update( updated_files )
|
68
|
+
delete_files_after_svn_update( updated_files )
|
69
|
+
add_files_after_svn_update( updated_files )
|
70
|
+
run_command( Git::Si::GitControl.commit_revision_command( get_svn_revision ) )
|
71
|
+
end
|
72
|
+
unstash_local_changes( stashed_changes )
|
73
|
+
success_message "fetch complete!"
|
74
|
+
end
|
75
|
+
|
76
|
+
def do_rebase_action
|
77
|
+
on_local_branch do
|
78
|
+
stashed_changes = stash_local_changes
|
79
|
+
run_command( Git::Si::GitControl.rebase_command( get_mirror_branch ) )
|
80
|
+
unstash_local_changes( stashed_changes )
|
81
|
+
success_message "rebase complete!"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def do_pull_action
|
86
|
+
do_fetch_action
|
87
|
+
do_rebase_action
|
88
|
+
end
|
89
|
+
|
90
|
+
def do_commit_action
|
91
|
+
local_branch = get_local_branch()
|
92
|
+
if local_branch == 'master'
|
93
|
+
error_message "Please do not commit changes on the master branch"
|
94
|
+
return
|
95
|
+
end
|
96
|
+
|
97
|
+
on_local_branch do
|
98
|
+
raise Git::Si::GitError.new("There are local changes; please commit them before continuing.") if are_there_git_changes?
|
99
|
+
|
100
|
+
notice_message "Adding any files that are not already in svn to ensure changes are committed."
|
101
|
+
do_readd_action
|
102
|
+
|
103
|
+
svn_diff = get_command_output( Git::Si::SvnControl.diff_command )
|
104
|
+
raise Git::Si::SvnError.new("There are no changes to commit.") if svn_diff.strip.empty?
|
105
|
+
|
106
|
+
run_command( Git::Si::SvnControl.commit_command )
|
107
|
+
success_message "commit complete!"
|
108
|
+
|
109
|
+
if are_there_git_changes? and yes?( "Some files were added or modified during the commit; should I revert them? [y/N] ", :yellow )
|
110
|
+
run_command( Git::Si::GitControl.hard_reset_command )
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
notice_message "Updating mirror branch to latest commit"
|
115
|
+
do_fetch_action
|
116
|
+
|
117
|
+
delete_committed_branch( local_branch ) if yes?( "Do you want to switch to the master branch and delete the committed branch '#{local_branch}'? [y/N] ", :green )
|
118
|
+
end
|
119
|
+
|
120
|
+
def do_init_action
|
121
|
+
on_local_branch do
|
122
|
+
# check for svn repo
|
123
|
+
run_command( Git::Si::SvnControl.info_command, { :allow_errors => true } )
|
124
|
+
raise Git::Si::SvnError.new("No svn repository was found here. Maybe you're in the wrong directory?") unless did_last_command_succeed?
|
125
|
+
|
126
|
+
# make sure svn repo is up-to-date
|
127
|
+
run_command( Git::Si::SvnControl.update_command )
|
128
|
+
|
129
|
+
make_a_commit = false
|
130
|
+
|
131
|
+
# check for existing .git repo
|
132
|
+
make_a_commit = true if create_git_repository()
|
133
|
+
|
134
|
+
# check for existing .gitignore
|
135
|
+
make_a_commit = true if create_gitignore()
|
136
|
+
|
137
|
+
# make initial commit
|
138
|
+
if make_a_commit
|
139
|
+
notice_message "Making initial commit."
|
140
|
+
run_command( Git::Si::GitControl.commit_revision_command(get_svn_revision) )
|
141
|
+
end
|
142
|
+
|
143
|
+
# check for exiting mirror branch
|
144
|
+
create_mirror_branch()
|
145
|
+
|
146
|
+
success_message "init complete!"
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Git
|
2
|
+
module Si
|
3
|
+
|
4
|
+
class GitSiError < StandardError
|
5
|
+
end
|
6
|
+
|
7
|
+
class ShellError < GitSiError
|
8
|
+
end
|
9
|
+
|
10
|
+
class GitError < GitSiError
|
11
|
+
end
|
12
|
+
|
13
|
+
class SvnError < GitSiError
|
14
|
+
end
|
15
|
+
|
16
|
+
class VersionError < GitSiError
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
require "git/si/version"
|
2
|
+
|
3
|
+
module Git
|
4
|
+
module Si
|
5
|
+
class GitControl
|
6
|
+
@@default_git_binary = 'git'
|
7
|
+
@@git_binary = 'git'
|
8
|
+
|
9
|
+
def self.git_binary=(binary)
|
10
|
+
@@git_binary = binary && binary.length > 0 ? binary : @@default_git_binary
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.status_command(*args)
|
14
|
+
command = "#{@@git_binary} status --porcelain"
|
15
|
+
if ( args.length > 0 )
|
16
|
+
command += " " + args.join(' ')
|
17
|
+
end
|
18
|
+
command
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.log_command(*args)
|
22
|
+
command = "#{@@git_binary} log"
|
23
|
+
if ( args.length > 0 )
|
24
|
+
command += " " + args.join(' ')
|
25
|
+
end
|
26
|
+
command
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.parse_last_svn_revision(info)
|
30
|
+
results = info.match(/svn update to version (\d+)/i)
|
31
|
+
return results[1] if results
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.add_command(*files)
|
35
|
+
raise GitSiError.new("Add command requires filenames") if ( files.length == 0 )
|
36
|
+
"#{@@git_binary} add " + files.join(' ')
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.are_there_changes?(status_output)
|
40
|
+
status_output.match(/^\s*[MADRC]/)
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.commit_revision_command(revision)
|
44
|
+
version = Git::Si::Version.version
|
45
|
+
"#{@@git_binary} commit --allow-empty -am 'git-si #{version} svn update to version #{revision}'"
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.stash_command
|
49
|
+
"#{@@git_binary} stash"
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.unstash_command
|
53
|
+
"#{@@git_binary} stash pop"
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.rebase_command(branch)
|
57
|
+
raise GitSiError.new("Rebase command requires branch name") if branch.empty?
|
58
|
+
"#{@@git_binary} rebase '#{branch}'"
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.checkout_command(branch)
|
62
|
+
raise GitSiError.new("Checkout command requires branch name") if branch.empty?
|
63
|
+
"#{@@git_binary} checkout #{branch}"
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.create_branch_command(branch)
|
67
|
+
raise GitSiError.new("New branch command requires branch name") if branch.empty?
|
68
|
+
"#{@@git_binary} branch #{branch}"
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.delete_branch_command(branch)
|
72
|
+
raise GitSiError.new("Delete branch command requires branch name") if branch.empty?
|
73
|
+
"#{@@git_binary} branch -D #{branch}"
|
74
|
+
end
|
75
|
+
|
76
|
+
def self.branch_command
|
77
|
+
"#{@@git_binary} branch"
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.parse_current_branch(git_branches)
|
81
|
+
results = git_branches.match(/^\*\s+(\S+)/)
|
82
|
+
return results[1] if results
|
83
|
+
end
|
84
|
+
|
85
|
+
def self.hard_reset_command
|
86
|
+
"#{@@git_binary} reset --hard HEAD"
|
87
|
+
end
|
88
|
+
|
89
|
+
def self.list_file_command(filename)
|
90
|
+
raise GitSiError.new("List file command requires filename") if filename.empty?
|
91
|
+
"#{@@git_binary} ls-files #{filename}"
|
92
|
+
end
|
93
|
+
|
94
|
+
def self.init_command
|
95
|
+
"#{@@git_binary} init"
|
96
|
+
end
|
97
|
+
|
98
|
+
def self.show_branch_command(branch)
|
99
|
+
raise GitSiError.new("Show branch command requires branch name") if branch.empty?
|
100
|
+
"#{@@git_binary} show-ref refs/heads/#{branch}"
|
101
|
+
end
|
102
|
+
|
103
|
+
def self.delete_command(filename)
|
104
|
+
raise GitSiError.new("Remove file command requires filename") if filename.empty?
|
105
|
+
"#{@@git_binary} rm #{filename}"
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Git
|
2
|
+
module Si
|
3
|
+
class GitIgnore
|
4
|
+
@@gitignore_patterns = [".*", "!.gitignore", ".svn", "*.sw?", ".config", "*.err", "*.pid", "*.log", "svn-commit.*", "*.orig", "node_modules"]
|
5
|
+
|
6
|
+
def self.ignore_patterns
|
7
|
+
@@gitignore_patterns
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.get_missing_lines_from(lines, patterns=@@gitignore_patterns)
|
11
|
+
patterns.reject do |pattern|
|
12
|
+
lines.detect do |line|
|
13
|
+
line.strip.eql? pattern.strip
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
|
@@ -0,0 +1,135 @@
|
|
1
|
+
module Git
|
2
|
+
module Si
|
3
|
+
class SvnControl
|
4
|
+
@@default_svn_binary = 'svn'
|
5
|
+
@@svn_binary = 'svn'
|
6
|
+
|
7
|
+
def self.svn_binary=(binary)
|
8
|
+
@@svn_binary = binary && binary.length > 0 ? binary : @@default_svn_binary
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.status_command(*args)
|
12
|
+
command = "#{@@svn_binary} status --ignore-externals"
|
13
|
+
if ( args.length > 0 )
|
14
|
+
command += " " + args.join(' ')
|
15
|
+
end
|
16
|
+
command
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.info_command
|
20
|
+
"#{@@svn_binary} info"
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.diff_command(*args)
|
24
|
+
command = "#{@@svn_binary} diff"
|
25
|
+
if ( args.length > 0 )
|
26
|
+
command += " " + args.join(' ')
|
27
|
+
end
|
28
|
+
command
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.parse_last_revision(svn_info)
|
32
|
+
results = svn_info.match(/^Revision:\s+(\d+)/)
|
33
|
+
return results[1] if results
|
34
|
+
return nil
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.parse_root_path(svn_info)
|
38
|
+
results = svn_info.match(/Root Path:\s+(.+)/)
|
39
|
+
return results[1] if results
|
40
|
+
return nil
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.parse_updated_files(svn_update_output)
|
44
|
+
svn_update_output.split(/\r?\n/).collect do |line|
|
45
|
+
line.strip.match(Regexp.union(/^\s*[AGU]\s+(\S.+)/, /^Restored '(.+)'/, /^Resolved conflicted state of '(.+)'/)) do |pattern|
|
46
|
+
pattern.to_a.compact.last
|
47
|
+
end
|
48
|
+
end.compact
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.parse_deleted_files(svn_update_output)
|
52
|
+
svn_update_output.split(/\r?\n/).collect do |line|
|
53
|
+
line.strip.match(Regexp.union(/^\s*D\s+(\S.+)/)) do |pattern|
|
54
|
+
pattern.to_a.compact.last
|
55
|
+
end
|
56
|
+
end.compact
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.parse_conflicted_files(svn_update_output)
|
60
|
+
svn_update_output.split(/\r?\n/).collect do |line|
|
61
|
+
line.strip.match(Regexp.union(/^\s*C\s+(\S.+)/, /^Resolved conflicted state of '(.+)'/)) do |pattern|
|
62
|
+
pattern.to_a.compact.last
|
63
|
+
end
|
64
|
+
end.compact
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.parse_unknown_files(svn_update_output)
|
68
|
+
svn_update_output.split(/\r?\n/).collect do |line|
|
69
|
+
line.strip.match(/^\s*\?\s+(\S.+)/) do |pattern|
|
70
|
+
pattern.to_a.compact.last
|
71
|
+
end
|
72
|
+
end.compact
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.add_command(*files)
|
76
|
+
raise GitSiError.new("Add command requires filenames") if ( files.length == 0 )
|
77
|
+
"#{@@svn_binary} add " + files.join(' ')
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.blame_command(*files)
|
81
|
+
raise GitSiError.new("Blame command requires filenames") if ( files.length == 0 )
|
82
|
+
"#{@@svn_binary} blame " + files.join(' ')
|
83
|
+
end
|
84
|
+
|
85
|
+
def self.update_command
|
86
|
+
"#{@@svn_binary} up --accept theirs-full --ignore-externals"
|
87
|
+
end
|
88
|
+
|
89
|
+
def self.revert_command(*args)
|
90
|
+
command = "#{@@svn_binary} revert -R"
|
91
|
+
if ( args.length > 0 )
|
92
|
+
command += " " + args.join(' ')
|
93
|
+
else
|
94
|
+
command += " ."
|
95
|
+
end
|
96
|
+
command
|
97
|
+
end
|
98
|
+
|
99
|
+
def self.commit_command(*args)
|
100
|
+
command = "#{@@svn_binary} commit"
|
101
|
+
if ( args.length > 0 )
|
102
|
+
command += " " + args.join(' ')
|
103
|
+
end
|
104
|
+
command
|
105
|
+
end
|
106
|
+
|
107
|
+
def self.list_file_command
|
108
|
+
"#{@@svn_binary} list -R"
|
109
|
+
end
|
110
|
+
|
111
|
+
def self.parse_file_list(list_output)
|
112
|
+
list_output.split(/\r?\n/).collect do |filename|
|
113
|
+
filename.strip if filename.strip !~ Regexp.union( /\/$/, /^\./, /\/\./ ) and not filename.empty?
|
114
|
+
end.compact
|
115
|
+
end
|
116
|
+
|
117
|
+
def self.parse_svn_status(status_string)
|
118
|
+
return '' unless status_string
|
119
|
+
status_string.split(/\r?\n/).select do |line|
|
120
|
+
line.strip !~ /(^X|\.git|\.swp$)/
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def self.parse_external_repos(status_string)
|
125
|
+
status_string.split(/\r?\n/).collect do |line|
|
126
|
+
line.strip.match(/^\s*X\s+(\S.+)/) do |pattern|
|
127
|
+
pattern.to_a.compact.last
|
128
|
+
end
|
129
|
+
end.compact
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|