gitti 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: cedf4859bb7b5521d3680c64f310f0a3456d1fbb
4
- data.tar.gz: 32e27bc777a0381db476082563b466d2438be218
3
+ metadata.gz: ef8765bec0642f4ee88c23169fffe92f8f0bba1a
4
+ data.tar.gz: a877ebe7e9bcff04915ed4fcc782dcf2c8000dc3
5
5
  SHA512:
6
- metadata.gz: 7b23e5ed7a99c1e632b0ccfecc7e9a3afc8aeee943884b72367e354e6829f2360cd3b8ba2f43e3bb8f54467fd262d72d7dcd54b26bfb29b647b7a9a93833c367
7
- data.tar.gz: ba2a18948bf92d98d9c4a09396ee95dcd05da757a3ce65aa633438530a43cdc133784054e3755fa72168eaa06c652beedc1aabeb8de73affd6a20b6f4b9c2d12
6
+ metadata.gz: 683017b302f2becfaa0fb76c6f2dbfecd3e961ef534d8c8b784070a2df90fefc4df38c476d9601ef0d2e0167b2c613adbe781e918520ba15dc2e8e0b33e0ece7
7
+ data.tar.gz: 41320be67f3b4f4a3dd34bdac9a7ac4a94b21d47012f84be53cb8dee0170c9acc811927c910e985983f724a7d7826fbd36b04f457c31b25c47320733af7ce832
@@ -4,6 +4,8 @@ README.md
4
4
  Rakefile
5
5
  lib/gitti.rb
6
6
  lib/gitti/base.rb
7
+ lib/gitti/git.rb
8
+ lib/gitti/mirror.rb
7
9
  lib/gitti/project.rb
8
10
  lib/gitti/reposet.rb
9
11
  lib/gitti/version.rb
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # gitti
2
2
 
3
- gitti gem - (yet) another (lite) git command line wrapper / library
3
+ gitti gem - (yet) another (lite) git command line helper / wrapper
4
4
 
5
5
  * home :: [github.com/rubycoco/gitti](https://github.com/rubycoco/gitti)
6
6
  * bugs :: [github.com/rubycoco/gitti/issues](https://github.com/rubycoco/gitti/issues)
@@ -11,7 +11,7 @@ gitti gem - (yet) another (lite) git command line wrapper / library
11
11
 
12
12
  ## Usage
13
13
 
14
- `Git` • `GitProject`
14
+ `Git` • `GitProject` • `GitMirror`
15
15
 
16
16
 
17
17
  ### `Git` Class
@@ -58,6 +58,17 @@ Git.add_all ## same as git --all
58
58
  Git.commit( "message" )
59
59
 
60
60
  Git.files ## same as git ls-tree --full-tree --name-only -r HEAD
61
+
62
+ Git.check ## same as git fsck
63
+ Git.fsck ## alias for check
64
+ Git.checksum ## another alias for check
65
+
66
+ Git.origin ## same as git remote show origin
67
+ Git.upstream ## same as git remote show upstream
68
+ Git.origin?
69
+ Git.upstream?
70
+
71
+
61
72
  Git.config( "user.name" ) ## use --get option
62
73
  Git.config( "user.name", show_origin: true ) ## add --show-origin flag
63
74
  Git.config( "user.name", show_scope: true ) ## add --show-scope flag
@@ -95,9 +106,28 @@ GitProject.open( "rubycoco/gitti" ) do |proj|
95
106
  proj.commit( "message" )
96
107
 
97
108
  proj.files
109
+
110
+ proj.origin
111
+ proj.upstream
112
+ proj.origin?
113
+ proj.upstream?
114
+ end
115
+ ```
116
+
117
+
118
+ ### `GitMirror` Class
119
+
120
+ Use the `GitMirror` class for existing mirrored (bare) git repo(sitories)
121
+ without workspace. Example:
122
+
123
+ ``` ruby
124
+ GitMirror.open( "rubycoco/gitti.git" ) do |mirror|
125
+ mirror.update # sames as git remote update
98
126
  end
99
127
  ```
100
128
 
129
+
130
+
101
131
  That's it for now.
102
132
 
103
133
 
data/Rakefile CHANGED
@@ -3,9 +3,9 @@ require './lib/gitti/version.rb'
3
3
 
4
4
  Hoe.spec 'gitti' do
5
5
 
6
- self.version = Gitti::VERSION
6
+ self.version = GittiCore::VERSION
7
7
 
8
- self.summary = 'gitti - (yet) another (lite) git command line wrapper / library'
8
+ self.summary = 'gitti - (yet) another (lite) git command line helper / wrapper'
9
9
  self.description = summary
10
10
 
11
11
  self.urls = { home: 'https://github.com/rubycoco/gitti' }
@@ -1,44 +1,7 @@
1
- require 'pp'
2
- require 'time'
3
- require 'date' ## e.g. Date.today etc.
4
- require 'yaml'
5
- require 'json'
6
- require 'uri'
7
- require 'net/http'
8
- require "net/https"
9
- require 'open3'
10
- require 'fileutils' ## e.g. FileUtils.mkdir_p etc.
1
+ require_relative 'gitti/base'
11
2
 
3
+ ## note: auto include Gitti; for "modular" version use ("Sinatra-style")
4
+ ## require "gitti/base"
12
5
 
6
+ include Gitti
13
7
 
14
- # our own code
15
- require 'gitti/version' # note: let version always go first
16
- require 'gitti/base'
17
- require 'gitti/project'
18
- require 'gitti/reposet'
19
-
20
-
21
-
22
- module Gitti
23
- ## todo: change to GitHubRepoRef or GitHubProject
24
- ## or Git::GitHub or Git::Source::GitHub or such - why? why not?
25
- class GitHubRepo
26
- attr_reader :owner, :name
27
-
28
- def initialize( owner, name )
29
- @owner = owner ## use/rename to login or something - why? why not??
30
- @name = name # e.g. "rubylibs/webservice"
31
- end
32
-
33
- def ssh_clone_url
34
- ## check: use https: as default? for github - http:// still supported? or redirected?
35
- ## "http://github.com/#{@owner}/#{@name}"
36
- "git@github.com:#{@owner}/#{@name}.git"
37
- end
38
- end ## class GitHubRepo
39
- end ## module Gitti
40
-
41
-
42
-
43
- # say hello
44
- puts Gitti.banner ## if defined?( $RUBYCOCO_DEBUG )
@@ -1,220 +1,57 @@
1
- module Gitti
2
-
3
- ## raised by Git::Shell.run -- check if top-level ShellError alread exists?
4
- ## use ShellError or RunError - why? why not?
5
- ## and make Git::Shell top-level e.g. Shell - why? why not?
6
- class GitError < StandardError
7
- end
8
-
9
-
10
- class Git ## make Git a module - why? why not?
11
-
12
- ###
13
- ## todo/fix: change opts=nil to *args or such - why? why not?
14
-
15
-
16
- ###############
17
- ## "setup" starter git commands
18
-
19
- def self.clone( repo, name=nil )
20
- cmd = "git clone #{repo}"
21
- cmd << " #{name}" unless name.nil? || name.empty?
22
- Shell.run( cmd )
23
- end
24
-
25
- def self.mirror( repo )
26
- cmd = "git clone --mirror #{repo}"
27
- Shell.run( cmd )
28
- end
29
-
30
-
31
- #################
32
- ## standard git commands
33
-
34
- def self.version
35
- cmd = 'git --version'
36
- Shell.run( cmd )
37
- end
38
-
39
- def self.status( short: false )
40
- cmd = 'git status'
41
- cmd << " --short" if short
42
- Shell.run( cmd )
43
- end
44
-
45
- def self.changes ## same as git status --short - keep shortcut / alias - why? why not?
46
- ## returns changed files - one per line or empty if no changes
47
- cmd = 'git status --short'
48
- Shell.run( cmd )
49
- end
50
-
51
- #####################
52
- ## status helpers
53
-
54
- ## git status --short returns empty stdout/list
55
- def self.clean?() changes.empty?; end
56
-
57
- def self.changes?() clean? == false; end ## reverse of clean?
58
- class << self
59
- alias_method :dirty?, :changes? ## add alias
60
- end
61
-
62
-
63
- #######
64
- ## more (major) git commands
65
-
66
- def self.fetch
67
- cmd = 'git fetch'
68
- Shell.run( cmd )
69
- end
70
-
71
- def self.pull
72
- cmd = 'git pull'
73
- Shell.run( cmd )
74
- end
75
-
76
- def self.fast_forward
77
- cmd = 'git pull --ff-only'
78
- Shell.run( cmd )
79
- end
80
- class << self
81
- alias_method :ff, :fast_forward ## add alias
82
- end
83
-
84
-
85
- def self.push
86
- cmd = 'git push'
87
- Shell.run( cmd )
88
- end
89
-
90
- def self.add( pathspec=nil ) ## e.g. git add . or git add *.rb or such
91
- cmd = 'git add'
92
- cmd << " #{pathspec}" unless pathspec.nil? || pathspec.empty?
93
- Shell.run( cmd )
94
- end
95
-
96
- def self.add_all
97
- cmd = 'git add --all'
98
- Shell.run( cmd )
99
- end
100
-
101
- def self.commit( message )
102
- ### todo/check: make message.nil? an ArgumentError - why? why not?
103
- ### if message.nil? || message.empty?
104
-
105
- cmd = 'git commit'
106
- cmd << %Q{ -m "#{message}"}
107
-
108
- Shell.run( cmd )
109
- end
110
-
111
-
112
- #############
113
- # change git ls-files to git ls-tree ... - why? why not?
114
- ## - note: git ls-files will include stages files too
115
- # not only committed ones!!!
116
- #
117
- # git ls-tree --full-tree --name-only -r HEAD
118
- # 1) --full-tree makes the command run as if you were in the repo's root directory.
119
- # 2) -r recurses into subdirectories. Combined with --full-tree, this gives you all committed, tracked files.
120
- # 3) --name-only removes SHA / permission info for when you just want the file paths.
121
- # 4) HEAD specifies which branch you want the list of tracked, committed files for.
122
- # You could change this to master or any other branch name, but HEAD is the commit you have checked out right now.
123
- #
124
- # see https://stackoverflow.com/questions/15606955/how-can-i-make-git-show-a-list-of-the-files-that-are-being-tracked
125
- #
126
- # was:
127
-
128
- def self.files ## was: e.g. git ls-files . or git ls-files *.rb or such
129
- ### todo/check: include --full-tree - why? why not?
130
- ## will ALWAYS list all files NOT depending on (current) working directory
131
-
132
- cmd = 'git ls-tree --full-tree --name-only -r HEAD' # was: 'git ls-files'
133
- Shell.run( cmd )
134
- end
135
- ## add list_files or ls_files alias - why? why not?
136
-
137
-
138
- ########
139
- ## query git configuration helpers
140
- def self.config( prop,
141
- show_origin: false,
142
- show_scope: false ) ## find a better name e.g. config_get? why? why not?
143
- cmd = "git config"
144
- cmd << " --show-origin" if show_origin
145
- cmd << " --show-scope" if show_scope
146
-
147
- if prop.is_a?( Regexp )
148
- ## note: use Regexp#source
149
- ## Returns the original string of the pattern.
150
- ## e.g. /ab+c/ix.source #=> "ab+c"
151
- ## Note that escape sequences are retained as is.
152
- ## /\x20\+/.source #=> "\\x20\\+"
153
- cmd << " --get-regexp #{prop.source}"
154
- else ## assume string
155
- cmd << " --get #{prop}"
156
- end
157
-
158
- Shell.run( cmd )
159
- end
160
-
161
-
162
- ### add more - why? why not?
163
- ##
164
- ## def remote_update( opts={} ) ## e.g. git remote update
165
- ## command "remote update"
166
- ## end
167
-
168
- ## todo/check: rename remote to shorthand/shortcut or something or to branch - why, why not??
169
- ## def remote_show( name='origin', opts={}) ## e.g. git remote show origin
170
- ## command "remote show #{name}"
171
- ## end
172
-
173
-
174
-
175
-
176
-
177
- ###
178
- # use nested class for "base" for running commands - why? why not?
179
- class Shell
180
- def self.run( cmd )
181
- print "cmd exec >#{cmd}<..."
182
- stdout, stderr, status = Open3.capture3( cmd )
183
-
184
- if status.success?
185
- print " OK"
186
- print "\n"
187
- else
188
- print " FAIL (#{status.exitstatus})"
189
- print "\n"
190
- end
191
-
192
- unless stdout.empty?
193
- puts stdout
194
- end
195
-
196
- unless stderr.empty?
197
- ## todo/check: or use >2: or &2: or such
198
- ## stderr output not always an error (that is, exit status might be 0)
199
- puts "STDERR:"
200
- puts stderr
201
- end
202
-
203
- if status.success?
204
- stdout # return stdout string
205
- else
206
- puts "!! ERROR: cmd exec >#{cmd}< failed with exit status #{status.exitstatus}:"
207
- puts stderr
208
-
209
- ### todo/fix: do NOT use GitError here!!! make it more "general"
210
- ### use a Git::Shell.run() wrapper or such - why? why not?
211
- ## or use a Shell.git() or Shell.git_run() ???
212
- ## or pass in error class - why? why not?
213
- raise GitError, "cmd exec >#{cmd}< failed with exit status #{status.exitstatus}<: #{stderr}"
214
- end
215
- end
216
- end # class Git::Shell
217
-
218
- end # class Git
219
-
220
- end # module Gitti
1
+ require 'pp'
2
+ require 'time'
3
+ require 'date' ## e.g. Date.today etc.
4
+ require 'yaml'
5
+ require 'json'
6
+ require 'uri'
7
+ require 'net/http'
8
+ require "net/https"
9
+ require 'open3'
10
+ require 'fileutils' ## e.g. FileUtils.mkdir_p etc.
11
+
12
+
13
+
14
+ # our own code
15
+ require 'gitti/version' # note: let version always go first
16
+ require 'gitti/git'
17
+ require 'gitti/project'
18
+ require 'gitti/mirror'
19
+ require 'gitti/reposet'
20
+
21
+
22
+
23
+ module Gitti
24
+ ## todo: change to GitHubRepoRef or GitHubProject
25
+ ## or Git::GitHub or Git::Source::GitHub or such - why? why not?
26
+ class GitHubRepo
27
+ attr_reader :owner, :name
28
+
29
+ def initialize( owner, name )
30
+ @owner = owner ## use/rename to login or something - why? why not??
31
+ @name = name # e.g. "rubylibs/webservice"
32
+ end
33
+
34
+
35
+ def ssh_clone_url
36
+ ## check: use https: as default? for github - http:// still supported? or redirected?
37
+ ## "http://github.com/#{@owner}/#{@name}"
38
+ "git@github.com:#{@owner}/#{@name}.git"
39
+ end
40
+
41
+ def http_clone_url ## use clone_url( http: true ) -- why? why not?
42
+ ## note: https is default for github - http:// gets redirected to https://
43
+ "http://github.com/#{@owner}/#{@name}"
44
+ end
45
+
46
+ def https_clone_url
47
+ "https://github.com/#{@owner}/#{@name}"
48
+ end
49
+
50
+
51
+ end ## class GitHubRepo
52
+ end ## module Gitti
53
+
54
+
55
+
56
+ # say hello
57
+ puts GittiCore.banner ## if defined?( $RUBYCOCO_DEBUG )
@@ -0,0 +1,290 @@
1
+ module Gitti
2
+
3
+ ## raised by Git::Shell.run -- check if top-level ShellError alread exists?
4
+ ## use ShellError or RunError - why? why not?
5
+ ## and make Git::Shell top-level e.g. Shell - why? why not?
6
+ class GitError < StandardError
7
+ end
8
+
9
+
10
+ class Git ## make Git a module - why? why not?
11
+
12
+ ###
13
+ ## todo/fix: change opts=nil to *args or such - why? why not?
14
+
15
+
16
+ ###############
17
+ ## "setup" starter git commands
18
+
19
+ def self.clone( repo, name=nil )
20
+ cmd = "git clone #{repo}"
21
+ cmd << " #{name}" unless name.nil? || name.empty?
22
+ Shell.run( cmd )
23
+ end
24
+
25
+ ###
26
+ ## What's the difference between git clone --mirror and git clone --bare
27
+ ## see https://stackoverflow.com/questions/3959924/whats-the-difference-between-git-clone-mirror-and-git-clone-bare
28
+ ##
29
+ ## The git clone help page has this to say about --mirror:
30
+ ## > Set up a mirror of the remote repository. This implies --bare
31
+ ##
32
+ ## The difference is that when using --mirror, all refs are copied as-is.
33
+ ## This means everything: remote-tracking branches, notes, refs/originals/*
34
+ ## (backups from filter-branch). The cloned repo has it all.
35
+ ## It's also set up so that a remote update will re-fetch everything from the origin
36
+ ## (overwriting the copied refs). The idea is really to mirror the repository,
37
+ ## to have a total copy, so that you could for example host your central repo
38
+ ## in multiple places, or back it up. Think of just straight-up copying the repo,
39
+ ## except in a much more elegant git way.
40
+ ##
41
+ ## The new documentation pretty much says all this:
42
+ ## see https://git-scm.com/docs/git-clone
43
+ ##
44
+ ## --mirror
45
+ ## Set up a mirror of the source repository. This implies --bare.
46
+ ## Compared to --bare, --mirror not only maps local branches of the source
47
+ ## to local branches of the target, it maps all refs
48
+ ## (including remote-tracking branches, notes etc.) and sets up a refspec configuration
49
+ ## such that all these refs are overwritten by a git remote update
50
+ ## in the target repository.
51
+ ##
52
+ ## More Articles / Resources:
53
+ ## https://docs.github.com/en/github/creating-cloning-and-archiving-repositories/duplicating-a-repository
54
+
55
+
56
+ ## add -n (--no-checkout) -- needed - why? why not?
57
+ ## add --no-hardlinks -- needed/recommended - why? why not?
58
+
59
+ def self.mirror( repo )
60
+ cmd = "git clone --mirror #{repo}"
61
+ Shell.run( cmd )
62
+ end
63
+
64
+
65
+ #################
66
+ ## standard git commands
67
+
68
+ def self.version
69
+ cmd = 'git --version'
70
+ Shell.run( cmd )
71
+ end
72
+
73
+ def self.status( short: false )
74
+ cmd = 'git status'
75
+ cmd << " --short" if short
76
+ Shell.run( cmd )
77
+ end
78
+
79
+ def self.changes ## same as git status --short - keep shortcut / alias - why? why not?
80
+ ## returns changed files - one per line or empty if no changes
81
+ cmd = 'git status --short'
82
+ Shell.run( cmd )
83
+ end
84
+
85
+ #####################
86
+ ## status helpers
87
+
88
+ ## git status --short returns empty stdout/list
89
+ def self.clean?() changes.empty?; end
90
+
91
+ def self.changes?() clean? == false; end ## reverse of clean?
92
+ class << self
93
+ alias_method :dirty?, :changes? ## add alias
94
+ end
95
+
96
+
97
+ #######
98
+ ## more (major) git commands
99
+
100
+ def self.fetch
101
+ cmd = 'git fetch'
102
+ Shell.run( cmd )
103
+ end
104
+
105
+ def self.pull
106
+ cmd = 'git pull'
107
+ Shell.run( cmd )
108
+ end
109
+
110
+ def self.fast_forward
111
+ cmd = 'git pull --ff-only'
112
+ Shell.run( cmd )
113
+ end
114
+ class << self
115
+ alias_method :ff, :fast_forward ## add alias
116
+ end
117
+
118
+
119
+ def self.push
120
+ cmd = 'git push'
121
+ Shell.run( cmd )
122
+ end
123
+
124
+ def self.add( *pathspecs ) ## e.g. git add . or git add *.rb or such
125
+ cmd = 'git add'
126
+ pathspecs = ['.'] if pathspecs.size == 0
127
+ cmd << " #{pathspecs.join('')}"
128
+ Shell.run( cmd )
129
+ end
130
+
131
+ def self.add_all
132
+ cmd = 'git add --all'
133
+ Shell.run( cmd )
134
+ end
135
+
136
+ def self.commit( message )
137
+ ### todo/check: make message.nil? an ArgumentError - why? why not?
138
+ ### if message.nil? || message.empty?
139
+
140
+ cmd = 'git commit'
141
+ cmd << %Q{ -m "#{message}"}
142
+
143
+ Shell.run( cmd )
144
+ end
145
+
146
+
147
+ #############
148
+ # change git ls-files to git ls-tree ... - why? why not?
149
+ ## - note: git ls-files will include stages files too
150
+ # not only committed ones!!!
151
+ #
152
+ # git ls-tree --full-tree --name-only -r HEAD
153
+ # 1) --full-tree makes the command run as if you were in the repo's root directory.
154
+ # 2) -r recurses into subdirectories. Combined with --full-tree, this gives you all committed, tracked files.
155
+ # 3) --name-only removes SHA / permission info for when you just want the file paths.
156
+ # 4) HEAD specifies which branch you want the list of tracked, committed files for.
157
+ # You could change this to master or any other branch name, but HEAD is the commit you have checked out right now.
158
+ #
159
+ # see https://stackoverflow.com/questions/15606955/how-can-i-make-git-show-a-list-of-the-files-that-are-being-tracked
160
+ #
161
+ # was:
162
+
163
+ def self.files ## was: e.g. git ls-files . or git ls-files *.rb or such
164
+ ### todo/check: include --full-tree - why? why not?
165
+ ## will ALWAYS list all files NOT depending on (current) working directory
166
+
167
+ cmd = 'git ls-tree --full-tree --name-only -r HEAD' # was: 'git ls-files'
168
+ Shell.run( cmd )
169
+ end
170
+ ## add list_files or ls_files alias - why? why not?
171
+
172
+
173
+ ########
174
+ ## query git configuration helpers
175
+ def self.config( prop,
176
+ show_origin: false,
177
+ show_scope: false ) ## find a better name e.g. config_get? why? why not?
178
+ cmd = "git config"
179
+ cmd << " --show-origin" if show_origin
180
+ cmd << " --show-scope" if show_scope
181
+
182
+ if prop.is_a?( Regexp )
183
+ ## note: use Regexp#source
184
+ ## Returns the original string of the pattern.
185
+ ## e.g. /ab+c/ix.source #=> "ab+c"
186
+ ## Note that escape sequences are retained as is.
187
+ ## /\x20\+/.source #=> "\\x20\\+"
188
+ cmd << " --get-regexp #{prop.source}"
189
+ else ## assume string
190
+ cmd << " --get #{prop}"
191
+ end
192
+
193
+ Shell.run( cmd )
194
+ end
195
+
196
+
197
+ ## git remote update will update all of your branches
198
+ ## set to track remote ones, but not merge any changes in.
199
+ ##
200
+ ## git fetch --all didn't exist at one time, so git remote update what more useful.
201
+ ## Now that --all has been added to git fetch, git remote update is not really necessary.
202
+ ##
203
+ ## Differences between git remote update and fetch?
204
+ ## Is git remote update the equivalent of git fetch?
205
+ ## see https://stackoverflow.com/questions/1856499/differences-between-git-remote-update-and-fetch/17512004#17512004
206
+ ##
207
+ ## git fetch learned --all and --multiple options,
208
+ ## to run fetch from many repositories,
209
+ ## and --prune option to remove remote tracking branches that went stale.
210
+ ## These make git remote update and git remote prune less necessary
211
+ ## (there is no plan to remove remote update nor remote prune, though).
212
+ def self.update
213
+ cmd = 'git remote update'
214
+ Shell.run( cmd )
215
+ end
216
+
217
+ def self.origin ## e.g. git remote show origin
218
+ cmd = "git remote show origin"
219
+ Shell.run( cmd )
220
+ end
221
+
222
+ def self.upstream ## e.g. git remote show origin
223
+ cmd = "git remote show upstream"
224
+ Shell.run( cmd )
225
+ end
226
+
227
+ def self.origin?
228
+ puts "todo/fix: check if remote origin is available/exists - how?"
229
+ end
230
+
231
+ def self.upstream?
232
+ puts "todo/fix: check if remote upstream is available/exists - how?"
233
+ end
234
+
235
+
236
+ def self.check ## e.g. git fsck - check/validate hash of objects
237
+ cmd = "git fsck"
238
+ Shell.run( cmd )
239
+ end
240
+ class << self
241
+ alias_method :fsck, :check ## add alias
242
+ alias_method :checksum, :check
243
+ end
244
+
245
+
246
+
247
+ ###
248
+ # use nested class for "base" for running commands - why? why not?
249
+ class Shell
250
+ def self.run( cmd )
251
+ print "cmd exec >#{cmd}<..."
252
+ stdout, stderr, status = Open3.capture3( cmd )
253
+
254
+ if status.success?
255
+ print " OK"
256
+ print "\n"
257
+ else
258
+ print " FAIL (#{status.exitstatus})"
259
+ print "\n"
260
+ end
261
+
262
+ unless stdout.empty?
263
+ puts stdout
264
+ end
265
+
266
+ unless stderr.empty?
267
+ ## todo/check: or use >2: or &2: or such
268
+ ## stderr output not always an error (that is, exit status might be 0)
269
+ puts "STDERR:"
270
+ puts stderr
271
+ end
272
+
273
+ if status.success?
274
+ stdout # return stdout string
275
+ else
276
+ puts "!! ERROR: cmd exec >#{cmd}< failed with exit status #{status.exitstatus}:"
277
+ puts stderr
278
+
279
+ ### todo/fix: do NOT use GitError here!!! make it more "general"
280
+ ### use a Git::Shell.run() wrapper or such - why? why not?
281
+ ## or use a Shell.git() or Shell.git_run() ???
282
+ ## or pass in error class - why? why not?
283
+ raise GitError, "cmd exec >#{cmd}< failed with exit status #{status.exitstatus}<: #{stderr}"
284
+ end
285
+ end
286
+ end # class Git::Shell
287
+
288
+ end # class Git
289
+
290
+ end # module Gitti
@@ -0,0 +1,33 @@
1
+ module Gitti
2
+
3
+ class GitMirror
4
+ def self.open( path, &blk )
5
+ new( path ).open( &blk )
6
+ end
7
+
8
+ def self.update( path ) ### all-in-one convenience shortcut
9
+ new( path).open { |mirror| mirror.update }
10
+ end
11
+
12
+
13
+
14
+ def initialize( path )
15
+ raise ArgumentError, "dir >#{path}< not found; dir MUST already exist for GitMirror class - sorry" unless Dir.exist?( path )
16
+ ## todo/check: check for more dirs and files e.g.
17
+ ## /info,/objects,/refs, /hooks, HEAD, config, description -- why? why not?
18
+ raise ArgumentError, "dir >#{path}/objects< not found; dir MUST already be initialized with git for GitMirror class - sorry" unless Dir.exist?( "#{path}/objects" )
19
+ @path = path
20
+ end
21
+
22
+
23
+ def open( &blk )
24
+ Dir.chdir( @path ) do
25
+ blk.call( self )
26
+ end
27
+ end
28
+
29
+ def update() Git.update; end
30
+
31
+ end # class GitMirror
32
+ end # module Gitti
33
+
@@ -35,14 +35,21 @@ class GitProject
35
35
 
36
36
  def push() Git.push; end
37
37
 
38
- def add( pathspec ) Git.add( pathspec ); end
38
+ def add( *pathspecs ) Git.add( *pathspecs ); end
39
39
  def add_all() Git.add_all; end
40
40
  def commit( message ) Git.commit( message ); end
41
41
 
42
42
  def files() Git.files; end
43
43
 
44
- def run( cmd ) Git::Shell.run( cmd ); end
45
44
 
45
+ ### remote show origin|upstream|etc.
46
+ def origin() Git.origin; end
47
+ def upstream() Git.upstream; end
48
+ def origin?() Git.origin?; end
49
+ def upstream?() Git.upstream?; end
50
+
51
+
52
+ def run( cmd ) Git::Shell.run( cmd ); end
46
53
  end # class GitProject
47
54
  end # module Gitti
48
55
 
@@ -4,8 +4,9 @@ module Gitti
4
4
 
5
5
  class GitRepoSet ## todo: rename to Hash/Dict/List/Map or use GitHubRepoSet ??
6
6
 
7
- def self.from_file( path ) ## todo/fix: change to self.read - why? why not?
8
- hash = YAML.load_file( path )
7
+ def self.read( path )
8
+ txt = File.open( path, 'r:utf-8') { |f| f.read }
9
+ hash = YAML.load( txt )
9
10
  new( hash )
10
11
  end
11
12
 
@@ -14,19 +15,24 @@ def initialize( hash )
14
15
  @hash = hash
15
16
  end
16
17
 
18
+ def size
19
+ ## sum up total number of repos
20
+ @size ||= @hash.reduce(0) {|sum,(_,names)| sum+= names.size; sum }
21
+ end
22
+
17
23
  def each
18
- @hash.each do |key_with_counter,values|
24
+ @hash.each do |org_with_counter,names|
19
25
 
20
26
  ## remove optional number from key e.g.
21
27
  ## mrhydescripts (3) => mrhydescripts
22
28
  ## footballjs (4) => footballjs
23
29
  ## etc.
24
30
 
25
- key = key_with_counter.sub( /\s+\([0-9]+\)/, '' )
31
+ org = org_with_counter.sub( /\([0-9]+\)/, '' ).strip
26
32
 
27
- puts " -- #{key_with_counter} [#{key}] --"
33
+ ## puts " -- #{key_with_counter} [#{key}] --"
28
34
 
29
- yield( key, values )
35
+ yield( org, names )
30
36
  end
31
37
  end
32
38
 
@@ -1,7 +1,13 @@
1
1
 
2
- module Gitti
2
+ ### note: use a different module for version (meta) info
3
+ ### that is, GittiCore and NOT Gitti
4
+ ### why? do NOT "pollute" Gitti with MAJOR, MINOR, PATH, and
5
+ ### self.banner, self.root, etc.
6
+
7
+
8
+ module GittiCore ## todo/check: rename GittiBase or GittiMeta or such - why? why not?
3
9
  MAJOR = 0 ## todo: namespace inside version or something - why? why not??
4
- MINOR = 3
10
+ MINOR = 4
5
11
  PATCH = 0
6
12
  VERSION = [MAJOR,MINOR,PATCH].join('.')
7
13
 
@@ -16,5 +22,5 @@ module Gitti
16
22
  def self.root
17
23
  "#{File.expand_path( File.dirname(File.dirname(File.dirname(__FILE__))) )}"
18
24
  end
19
- end # module Gitti
25
+ end # module GittiCore
20
26
 
@@ -6,7 +6,6 @@ require 'helper'
6
6
 
7
7
  class TestBase < MiniTest::Test
8
8
 
9
- include Gitti
10
9
 
11
10
  def test_git_config
12
11
  puts "---"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gitti
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gerald Bauer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-09-05 00:00:00.000000000 Z
11
+ date: 2020-09-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rdoc
@@ -38,7 +38,7 @@ dependencies:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: '3.16'
41
- description: gitti - (yet) another (lite) git command line wrapper / library
41
+ description: gitti - (yet) another (lite) git command line helper / wrapper
42
42
  email: ruby-talk@ruby-lang.org
43
43
  executables: []
44
44
  extensions: []
@@ -53,6 +53,8 @@ files:
53
53
  - Rakefile
54
54
  - lib/gitti.rb
55
55
  - lib/gitti/base.rb
56
+ - lib/gitti/git.rb
57
+ - lib/gitti/mirror.rb
56
58
  - lib/gitti/project.rb
57
59
  - lib/gitti/reposet.rb
58
60
  - lib/gitti/version.rb
@@ -83,5 +85,5 @@ rubyforge_project:
83
85
  rubygems_version: 2.5.2
84
86
  signing_key:
85
87
  specification_version: 4
86
- summary: gitti - (yet) another (lite) git command line wrapper / library
88
+ summary: gitti - (yet) another (lite) git command line helper / wrapper
87
89
  test_files: []