gitti 0.3.0 → 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 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: []