gitti 0.2.0 → 0.4.2
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/{HISTORY.md → CHANGELOG.md} +0 -0
- data/Manifest.txt +8 -3
- data/README.md +150 -4
- data/Rakefile +6 -8
- data/lib/gitti.rb +4 -21
- data/lib/gitti/base.rb +57 -0
- data/lib/gitti/git.rb +316 -0
- data/lib/gitti/mirror.rb +33 -0
- data/lib/gitti/project.rb +61 -0
- data/lib/gitti/reposet.rb +42 -0
- data/lib/gitti/version.rb +10 -5
- data/test/helper.rb +7 -0
- data/test/test_base.rb +43 -0
- metadata +18 -27
- data/lib/gitti/lib.rb +0 -83
- data/lib/gitti/support/reposet.rb +0 -37
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 651f519f3b5f3ecf8a13a5125554cac2d69790f6
|
4
|
+
data.tar.gz: 7ae7285e8ac625336869c7d4de1d078c11c5ee6d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7c002bac4f5622a3083b27fa2e29d69736821d14c7b521ecd1a00b06cd62d80cae762e853e96943baa242e77568043a356fecc8c036c8856f18c8eac42facf0c
|
7
|
+
data.tar.gz: 694934d8ea8263d11b90f7a14a01f8f59d3d71a695b728ebd82927e66d554cf1e59195199af7c960e930cc9cb4d9ad4c3d93abec28df9fe1aca931304e1f4698
|
data/{HISTORY.md → CHANGELOG.md}
RENAMED
File without changes
|
data/Manifest.txt
CHANGED
@@ -1,8 +1,13 @@
|
|
1
|
-
|
1
|
+
CHANGELOG.md
|
2
2
|
Manifest.txt
|
3
3
|
README.md
|
4
4
|
Rakefile
|
5
5
|
lib/gitti.rb
|
6
|
-
lib/gitti/
|
7
|
-
lib/gitti/
|
6
|
+
lib/gitti/base.rb
|
7
|
+
lib/gitti/git.rb
|
8
|
+
lib/gitti/mirror.rb
|
9
|
+
lib/gitti/project.rb
|
10
|
+
lib/gitti/reposet.rb
|
8
11
|
lib/gitti/version.rb
|
12
|
+
test/helper.rb
|
13
|
+
test/test_base.rb
|
data/README.md
CHANGED
@@ -1,16 +1,162 @@
|
|
1
1
|
# gitti
|
2
2
|
|
3
|
-
gitti gem - (yet) another (lite) git command line
|
3
|
+
gitti gem - (yet) another (lite) git command line helper / wrapper
|
4
4
|
|
5
|
-
* home :: [github.com/
|
6
|
-
* bugs :: [github.com/
|
5
|
+
* home :: [github.com/rubycoco/gitti](https://github.com/rubycoco/gitti)
|
6
|
+
* bugs :: [github.com/rubycoco/gitti/issues](https://github.com/rubycoco/gitti/issues)
|
7
7
|
* gem :: [rubygems.org/gems/gitti](https://rubygems.org/gems/gitti)
|
8
8
|
* rdoc :: [rubydoc.info/gems/gitti](http://rubydoc.info/gems/gitti)
|
9
9
|
|
10
10
|
|
11
|
+
|
11
12
|
## Usage
|
12
13
|
|
13
|
-
|
14
|
+
`Git` • `GitProject` • `GitMirror`
|
15
|
+
|
16
|
+
|
17
|
+
### `Git` Class
|
18
|
+
|
19
|
+
Use the `Git` class for "low-level / to the metal" git commands
|
20
|
+
that run in your current working directory.
|
21
|
+
Example:
|
22
|
+
|
23
|
+
``` ruby
|
24
|
+
|
25
|
+
###############
|
26
|
+
## "setup" starter git commands
|
27
|
+
|
28
|
+
Git.clone( "https://github.com/rubycoco/gitti.git" )
|
29
|
+
Git.clone( "https://github.com/rubycoco/gitti.git", "gitti-clone" )
|
30
|
+
# -or- -- if you have write / commit access use ssh
|
31
|
+
Git.clone( "git@github.com:rubycoco/gitti.git" )
|
32
|
+
Git.clone( "git@github.com:rubycoco/gitti.git", "gitti-clone" )
|
33
|
+
|
34
|
+
Git.mirror( "https://github.com/rubycoco/gitti.git" ) ## same as git clone --mirror
|
35
|
+
|
36
|
+
#################
|
37
|
+
## standard git commands
|
38
|
+
|
39
|
+
Git.version ## same as git --version
|
40
|
+
Git.status
|
41
|
+
Git.status( short: true ) ## same as Git.changes
|
42
|
+
Git.changes ## same as git status --short
|
43
|
+
|
44
|
+
#####################
|
45
|
+
## status helpers
|
46
|
+
|
47
|
+
Git.clean?
|
48
|
+
Git.changes?
|
49
|
+
Git.dirty? ## alias for changes?
|
50
|
+
|
51
|
+
#######
|
52
|
+
## more (major) git commands
|
53
|
+
|
54
|
+
Git.fetch
|
55
|
+
Git.pull
|
56
|
+
Git.fast_forward ## same as git pull --ff-only
|
57
|
+
Git.ff ## alias for fast_forward
|
58
|
+
Git.push
|
59
|
+
Git.add( "pathspec" )
|
60
|
+
Git.add_all ## same as git --all
|
61
|
+
Git.commit( "message" )
|
62
|
+
|
63
|
+
Git.files ## same as git ls-tree --full-tree --name-only -r HEAD
|
64
|
+
|
65
|
+
Git.check ## same as git fsck
|
66
|
+
Git.fsck ## alias for check
|
67
|
+
Git.checksum ## another alias for check
|
68
|
+
|
69
|
+
Git.master? ## on master branch
|
70
|
+
Git.main? ## on main branch
|
71
|
+
|
72
|
+
Git.origin ## same as git remote show origin
|
73
|
+
Git.upstream ## same as git remote show upstream
|
74
|
+
Git.origin?
|
75
|
+
Git.upstream?
|
76
|
+
|
77
|
+
Git.config( "user.name" ) ## use --get option
|
78
|
+
Git.config( "user.name", show_origin: true ) ## add --show-origin flag
|
79
|
+
Git.config( "user.name", show_scope: true ) ## add --show-scope flag
|
80
|
+
|
81
|
+
Git.config( /user/ ) ## use --get-regexp option
|
82
|
+
Git.config( /user/, show_origin: true ) ## add --show-origin flag
|
83
|
+
Git.config( /user/, show_scope: true ) ## add --show-scope flag
|
84
|
+
```
|
85
|
+
|
86
|
+
|
87
|
+
|
88
|
+
### `GitProject` Class
|
89
|
+
|
90
|
+
Use the `GitProject` class for existing git repo(sitories)
|
91
|
+
with workspace. Example:
|
92
|
+
|
93
|
+
``` ruby
|
94
|
+
GitProject.open( "rubycoco/gitti" ) do |proj|
|
95
|
+
proj.status
|
96
|
+
proj.status( short: true )
|
97
|
+
proj.changes
|
98
|
+
proj.clean?
|
99
|
+
proj.changes?
|
100
|
+
proj.dirty?
|
101
|
+
|
102
|
+
proj.fetch
|
103
|
+
proj.pull
|
104
|
+
proj.fast_forward
|
105
|
+
proj.ff
|
106
|
+
|
107
|
+
proj.push
|
108
|
+
|
109
|
+
proj.add( "pathspec" )
|
110
|
+
proj.add_all
|
111
|
+
proj.commit( "message" )
|
112
|
+
|
113
|
+
proj.files
|
114
|
+
|
115
|
+
proj.master?
|
116
|
+
proj.main?
|
117
|
+
|
118
|
+
proj.origin
|
119
|
+
proj.upstream
|
120
|
+
proj.origin?
|
121
|
+
proj.upstream?
|
122
|
+
end
|
123
|
+
```
|
124
|
+
|
125
|
+
|
126
|
+
### `GitMirror` Class
|
127
|
+
|
128
|
+
Use the `GitMirror` class for existing mirrored (bare) git repo(sitories)
|
129
|
+
without workspace. Example:
|
130
|
+
|
131
|
+
``` ruby
|
132
|
+
GitMirror.open( "rubycoco/gitti.git" ) do |mirror|
|
133
|
+
mirror.update # sames as git remote update
|
134
|
+
end
|
135
|
+
```
|
136
|
+
|
137
|
+
|
138
|
+
|
139
|
+
That's it for now.
|
140
|
+
|
141
|
+
|
142
|
+
|
143
|
+
## Real World Usage
|
144
|
+
|
145
|
+
The [`monos`](https://github.com/rubycoco/monos) gem incl. some monorepo / mono source tree tools and (startup) scripts
|
146
|
+
that let you run git commands on multiple repos.
|
147
|
+
|
148
|
+
|
149
|
+
|
150
|
+
## Installation
|
151
|
+
|
152
|
+
Use
|
153
|
+
|
154
|
+
gem install gitti
|
155
|
+
|
156
|
+
or add to your Gemfile
|
157
|
+
|
158
|
+
gem 'gitti'
|
159
|
+
|
14
160
|
|
15
161
|
|
16
162
|
## License
|
data/Rakefile
CHANGED
@@ -3,28 +3,26 @@ require './lib/gitti/version.rb'
|
|
3
3
|
|
4
4
|
Hoe.spec 'gitti' do
|
5
5
|
|
6
|
-
self.version =
|
6
|
+
self.version = GittiCore::VERSION
|
7
7
|
|
8
|
-
self.summary = 'gitti - (yet) another (lite) git command line
|
8
|
+
self.summary = 'gitti - (yet) another (lite) git command line helper / wrapper'
|
9
9
|
self.description = summary
|
10
10
|
|
11
|
-
self.urls =
|
11
|
+
self.urls = { home: 'https://github.com/rubycoco/gitti' }
|
12
12
|
|
13
13
|
self.author = 'Gerald Bauer'
|
14
14
|
self.email = 'ruby-talk@ruby-lang.org'
|
15
15
|
|
16
16
|
# switch extension to .markdown for gihub formatting
|
17
17
|
self.readme_file = 'README.md'
|
18
|
-
self.history_file = '
|
18
|
+
self.history_file = 'CHANGELOG.md'
|
19
19
|
|
20
|
-
self.extra_deps = [
|
21
|
-
['logutils' ],
|
22
|
-
]
|
20
|
+
self.extra_deps = []
|
23
21
|
|
24
22
|
self.licenses = ['Public Domain']
|
25
23
|
|
26
24
|
self.spec_extras = {
|
27
|
-
required_ruby_version: '>=
|
25
|
+
required_ruby_version: '>= 2.2.2'
|
28
26
|
}
|
29
27
|
|
30
28
|
end
|
data/lib/gitti.rb
CHANGED
@@ -1,24 +1,7 @@
|
|
1
|
-
|
1
|
+
require_relative 'gitti/base'
|
2
2
|
|
3
|
-
|
4
|
-
require "
|
5
|
-
require 'uri'
|
3
|
+
## note: auto include Gitti; for "modular" version use ("Sinatra-style")
|
4
|
+
## require "gitti/base"
|
6
5
|
|
7
|
-
|
8
|
-
require 'json'
|
9
|
-
require 'yaml'
|
6
|
+
include Gitti
|
10
7
|
|
11
|
-
|
12
|
-
# 3rd party gems/libs
|
13
|
-
require 'logutils'
|
14
|
-
|
15
|
-
# our own code
|
16
|
-
require 'gitti/version' # note: let version always go first
|
17
|
-
require 'gitti/lib'
|
18
|
-
|
19
|
-
## todo/check: move to its own gem e.g. gitti-support later - why? why not??
|
20
|
-
require 'gitti/support/reposet'
|
21
|
-
|
22
|
-
|
23
|
-
# say hello
|
24
|
-
puts Gitti.banner if defined?($RUBYLIBS_DEBUG)
|
data/lib/gitti/base.rb
ADDED
@@ -0,0 +1,57 @@
|
|
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 )
|
data/lib/gitti/git.rb
ADDED
@@ -0,0 +1,316 @@
|
|
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, depth: nil )
|
20
|
+
cmd = "git clone"
|
21
|
+
cmd << " --depth #{depth}" unless depth.nil?
|
22
|
+
cmd << " #{repo}"
|
23
|
+
cmd << " #{name}" unless name.nil? || name.empty?
|
24
|
+
Shell.run( cmd )
|
25
|
+
end
|
26
|
+
|
27
|
+
###
|
28
|
+
## What's the difference between git clone --mirror and git clone --bare
|
29
|
+
## see https://stackoverflow.com/questions/3959924/whats-the-difference-between-git-clone-mirror-and-git-clone-bare
|
30
|
+
##
|
31
|
+
## The git clone help page has this to say about --mirror:
|
32
|
+
## > Set up a mirror of the remote repository. This implies --bare
|
33
|
+
##
|
34
|
+
## The difference is that when using --mirror, all refs are copied as-is.
|
35
|
+
## This means everything: remote-tracking branches, notes, refs/originals/*
|
36
|
+
## (backups from filter-branch). The cloned repo has it all.
|
37
|
+
## It's also set up so that a remote update will re-fetch everything from the origin
|
38
|
+
## (overwriting the copied refs). The idea is really to mirror the repository,
|
39
|
+
## to have a total copy, so that you could for example host your central repo
|
40
|
+
## in multiple places, or back it up. Think of just straight-up copying the repo,
|
41
|
+
## except in a much more elegant git way.
|
42
|
+
##
|
43
|
+
## The new documentation pretty much says all this:
|
44
|
+
## see https://git-scm.com/docs/git-clone
|
45
|
+
##
|
46
|
+
## --mirror
|
47
|
+
## Set up a mirror of the source repository. This implies --bare.
|
48
|
+
## Compared to --bare, --mirror not only maps local branches of the source
|
49
|
+
## to local branches of the target, it maps all refs
|
50
|
+
## (including remote-tracking branches, notes etc.) and sets up a refspec configuration
|
51
|
+
## such that all these refs are overwritten by a git remote update
|
52
|
+
## in the target repository.
|
53
|
+
##
|
54
|
+
## More Articles / Resources:
|
55
|
+
## https://docs.github.com/en/github/creating-cloning-and-archiving-repositories/duplicating-a-repository
|
56
|
+
|
57
|
+
|
58
|
+
## add -n (--no-checkout) -- needed - why? why not?
|
59
|
+
## add --no-hardlinks -- needed/recommended - why? why not?
|
60
|
+
|
61
|
+
def self.mirror( repo )
|
62
|
+
cmd = "git clone --mirror #{repo}"
|
63
|
+
Shell.run( cmd )
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
#################
|
68
|
+
## standard git commands
|
69
|
+
|
70
|
+
def self.version
|
71
|
+
cmd = 'git --version'
|
72
|
+
Shell.run( cmd )
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.status( short: false )
|
76
|
+
cmd = 'git status'
|
77
|
+
cmd << " --short" if short
|
78
|
+
Shell.run( cmd )
|
79
|
+
end
|
80
|
+
|
81
|
+
def self.changes ## same as git status --short - keep shortcut / alias - why? why not?
|
82
|
+
## returns changed files - one per line or empty if no changes
|
83
|
+
cmd = 'git status --short'
|
84
|
+
Shell.run( cmd )
|
85
|
+
end
|
86
|
+
|
87
|
+
#####################
|
88
|
+
## status helpers
|
89
|
+
|
90
|
+
## git status --short returns empty stdout/list
|
91
|
+
def self.clean?() changes.empty?; end
|
92
|
+
|
93
|
+
def self.changes?() clean? == false; end ## reverse of clean?
|
94
|
+
class << self
|
95
|
+
alias_method :dirty?, :changes? ## add alias
|
96
|
+
end
|
97
|
+
|
98
|
+
|
99
|
+
#######
|
100
|
+
## more (major) git commands
|
101
|
+
|
102
|
+
def self.fetch
|
103
|
+
cmd = 'git fetch'
|
104
|
+
Shell.run( cmd )
|
105
|
+
end
|
106
|
+
|
107
|
+
def self.pull
|
108
|
+
cmd = 'git pull'
|
109
|
+
Shell.run( cmd )
|
110
|
+
end
|
111
|
+
|
112
|
+
def self.fast_forward
|
113
|
+
cmd = 'git pull --ff-only'
|
114
|
+
Shell.run( cmd )
|
115
|
+
end
|
116
|
+
class << self
|
117
|
+
alias_method :ff, :fast_forward ## add alias
|
118
|
+
end
|
119
|
+
|
120
|
+
|
121
|
+
def self.push
|
122
|
+
cmd = 'git push'
|
123
|
+
Shell.run( cmd )
|
124
|
+
end
|
125
|
+
|
126
|
+
def self.add( *pathspecs ) ## e.g. git add . or git add *.rb or such
|
127
|
+
cmd = 'git add'
|
128
|
+
pathspecs = ['.'] if pathspecs.size == 0
|
129
|
+
cmd << " #{pathspecs.join('')}"
|
130
|
+
Shell.run( cmd )
|
131
|
+
end
|
132
|
+
|
133
|
+
def self.add_all
|
134
|
+
cmd = 'git add --all'
|
135
|
+
Shell.run( cmd )
|
136
|
+
end
|
137
|
+
|
138
|
+
def self.commit( message )
|
139
|
+
### todo/check: make message.nil? an ArgumentError - why? why not?
|
140
|
+
### if message.nil? || message.empty?
|
141
|
+
|
142
|
+
cmd = 'git commit'
|
143
|
+
cmd << %Q{ -m "#{message}"}
|
144
|
+
|
145
|
+
Shell.run( cmd )
|
146
|
+
end
|
147
|
+
|
148
|
+
|
149
|
+
#############
|
150
|
+
# change git ls-files to git ls-tree ... - why? why not?
|
151
|
+
## - note: git ls-files will include stages files too
|
152
|
+
# not only committed ones!!!
|
153
|
+
#
|
154
|
+
# git ls-tree --full-tree --name-only -r HEAD
|
155
|
+
# 1) --full-tree makes the command run as if you were in the repo's root directory.
|
156
|
+
# 2) -r recurses into subdirectories. Combined with --full-tree, this gives you all committed, tracked files.
|
157
|
+
# 3) --name-only removes SHA / permission info for when you just want the file paths.
|
158
|
+
# 4) HEAD specifies which branch you want the list of tracked, committed files for.
|
159
|
+
# You could change this to master or any other branch name, but HEAD is the commit you have checked out right now.
|
160
|
+
#
|
161
|
+
# see https://stackoverflow.com/questions/15606955/how-can-i-make-git-show-a-list-of-the-files-that-are-being-tracked
|
162
|
+
#
|
163
|
+
# was:
|
164
|
+
|
165
|
+
def self.files ## was: e.g. git ls-files . or git ls-files *.rb or such
|
166
|
+
### todo/check: include --full-tree - why? why not?
|
167
|
+
## will ALWAYS list all files NOT depending on (current) working directory
|
168
|
+
|
169
|
+
cmd = 'git ls-tree --full-tree --name-only -r HEAD' # was: 'git ls-files'
|
170
|
+
Shell.run( cmd )
|
171
|
+
end
|
172
|
+
## add list_files or ls_files alias - why? why not?
|
173
|
+
|
174
|
+
|
175
|
+
########
|
176
|
+
## query git configuration helpers
|
177
|
+
def self.config( prop,
|
178
|
+
show_origin: false,
|
179
|
+
show_scope: false ) ## find a better name e.g. config_get? why? why not?
|
180
|
+
cmd = "git config"
|
181
|
+
cmd << " --show-origin" if show_origin
|
182
|
+
cmd << " --show-scope" if show_scope
|
183
|
+
|
184
|
+
if prop.is_a?( Regexp )
|
185
|
+
## note: use Regexp#source
|
186
|
+
## Returns the original string of the pattern.
|
187
|
+
## e.g. /ab+c/ix.source #=> "ab+c"
|
188
|
+
## Note that escape sequences are retained as is.
|
189
|
+
## /\x20\+/.source #=> "\\x20\\+"
|
190
|
+
cmd << " --get-regexp #{prop.source}"
|
191
|
+
else ## assume string
|
192
|
+
cmd << " --get #{prop}"
|
193
|
+
end
|
194
|
+
|
195
|
+
Shell.run( cmd )
|
196
|
+
end
|
197
|
+
|
198
|
+
|
199
|
+
def self.branch
|
200
|
+
cmd = 'git branch'
|
201
|
+
Shell.run( cmd )
|
202
|
+
end
|
203
|
+
|
204
|
+
def self.master?
|
205
|
+
output = branch ## check for '* master'
|
206
|
+
output.split( /\r?\n/ ).include?( '* master' )
|
207
|
+
end
|
208
|
+
|
209
|
+
def self.main?
|
210
|
+
output = branch ## check for '* main'
|
211
|
+
output.split( /\r?\n/ ).include?('* main')
|
212
|
+
end
|
213
|
+
|
214
|
+
## git remote update will update all of your branches
|
215
|
+
## set to track remote ones, but not merge any changes in.
|
216
|
+
##
|
217
|
+
## git fetch --all didn't exist at one time, so git remote update what more useful.
|
218
|
+
## Now that --all has been added to git fetch, git remote update is not really necessary.
|
219
|
+
##
|
220
|
+
## Differences between git remote update and fetch?
|
221
|
+
## Is git remote update the equivalent of git fetch?
|
222
|
+
## see https://stackoverflow.com/questions/1856499/differences-between-git-remote-update-and-fetch/17512004#17512004
|
223
|
+
##
|
224
|
+
## git fetch learned --all and --multiple options,
|
225
|
+
## to run fetch from many repositories,
|
226
|
+
## and --prune option to remove remote tracking branches that went stale.
|
227
|
+
## These make git remote update and git remote prune less necessary
|
228
|
+
## (there is no plan to remove remote update nor remote prune, though).
|
229
|
+
def self.update
|
230
|
+
cmd = 'git remote update'
|
231
|
+
Shell.run( cmd )
|
232
|
+
end
|
233
|
+
|
234
|
+
|
235
|
+
def self.origin ## e.g. git remote show origin
|
236
|
+
cmd = "git remote show origin"
|
237
|
+
Shell.run( cmd )
|
238
|
+
end
|
239
|
+
|
240
|
+
def self.upstream ## e.g. git remote show origin
|
241
|
+
cmd = "git remote show upstream"
|
242
|
+
Shell.run( cmd )
|
243
|
+
end
|
244
|
+
|
245
|
+
def self.remote
|
246
|
+
cmd = "git remote"
|
247
|
+
Shell.run( cmd )
|
248
|
+
end
|
249
|
+
|
250
|
+
def self.origin?
|
251
|
+
output = remote ## check for 'origin'
|
252
|
+
output.split( /\r?\n/ ).include?( 'origin' )
|
253
|
+
end
|
254
|
+
|
255
|
+
def self.upstream?
|
256
|
+
output = remote ## check for 'upstream'
|
257
|
+
output.split( /\r?\n/ ).include?( 'upstream' )
|
258
|
+
end
|
259
|
+
|
260
|
+
|
261
|
+
|
262
|
+
def self.check ## e.g. git fsck - check/validate hash of objects
|
263
|
+
cmd = "git fsck"
|
264
|
+
Shell.run( cmd )
|
265
|
+
end
|
266
|
+
class << self
|
267
|
+
alias_method :fsck, :check ## add alias
|
268
|
+
alias_method :checksum, :check
|
269
|
+
end
|
270
|
+
|
271
|
+
|
272
|
+
|
273
|
+
###
|
274
|
+
# use nested class for "base" for running commands - why? why not?
|
275
|
+
class Shell
|
276
|
+
def self.run( cmd )
|
277
|
+
print "cmd exec >#{cmd}<..."
|
278
|
+
stdout, stderr, status = Open3.capture3( cmd )
|
279
|
+
|
280
|
+
if status.success?
|
281
|
+
print " OK"
|
282
|
+
print "\n"
|
283
|
+
else
|
284
|
+
print " FAIL (#{status.exitstatus})"
|
285
|
+
print "\n"
|
286
|
+
end
|
287
|
+
|
288
|
+
unless stdout.empty?
|
289
|
+
puts stdout
|
290
|
+
end
|
291
|
+
|
292
|
+
unless stderr.empty?
|
293
|
+
## todo/check: or use >2: or &2: or such
|
294
|
+
## stderr output not always an error (that is, exit status might be 0)
|
295
|
+
puts "2>"
|
296
|
+
puts stderr
|
297
|
+
end
|
298
|
+
|
299
|
+
if status.success?
|
300
|
+
stdout # return stdout string
|
301
|
+
else
|
302
|
+
puts "!! ERROR: cmd exec >#{cmd}< failed with exit status #{status.exitstatus}:"
|
303
|
+
puts stderr
|
304
|
+
|
305
|
+
### todo/fix: do NOT use GitError here!!! make it more "general"
|
306
|
+
### use a Git::Shell.run() wrapper or such - why? why not?
|
307
|
+
## or use a Shell.git() or Shell.git_run() ???
|
308
|
+
## or pass in error class - why? why not?
|
309
|
+
raise GitError, "cmd exec >#{cmd}< failed with exit status #{status.exitstatus}<: #{stderr}"
|
310
|
+
end
|
311
|
+
end
|
312
|
+
end # class Git::Shell
|
313
|
+
|
314
|
+
end # class Git
|
315
|
+
|
316
|
+
end # module Gitti
|
data/lib/gitti/mirror.rb
ADDED
@@ -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
|
+
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module Gitti
|
2
|
+
|
3
|
+
class GitProject
|
4
|
+
def self.open( path, &blk )
|
5
|
+
new( path ).open( &blk )
|
6
|
+
end
|
7
|
+
|
8
|
+
def initialize( path )
|
9
|
+
raise ArgumentError, "dir >#{path}< not found; dir MUST already exist for GitProject class - sorry" unless Dir.exist?( path )
|
10
|
+
raise ArgumentError, "dir >#{path}/.git< not found; dir MUST already be initialized with git for GitProject class - sorry" unless Dir.exist?( "#{path}/.git" )
|
11
|
+
@path = path
|
12
|
+
end
|
13
|
+
|
14
|
+
|
15
|
+
def open( &blk )
|
16
|
+
## puts "Dir.getwd: #{Dir.getwd}"
|
17
|
+
Dir.chdir( @path ) do
|
18
|
+
blk.call( self )
|
19
|
+
end
|
20
|
+
## puts "Dir.getwd: #{Dir.getwd}"
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
def status( short: false ) Git.status( short: short ); end
|
25
|
+
def changes() Git.changes; end
|
26
|
+
def clean?() Git.clean?; end
|
27
|
+
def changes?() Git.changes?; end
|
28
|
+
alias_method :dirty?, :changes?
|
29
|
+
|
30
|
+
|
31
|
+
def fetch() Git.fetch; end
|
32
|
+
def pull() Git.pull; end
|
33
|
+
def fast_forward() Git.fast_forward; end
|
34
|
+
alias_method :ff, :fast_forward
|
35
|
+
|
36
|
+
def push() Git.push; end
|
37
|
+
|
38
|
+
def add( *pathspecs ) Git.add( *pathspecs ); end
|
39
|
+
def add_all() Git.add_all; end
|
40
|
+
def commit( message ) Git.commit( message ); end
|
41
|
+
|
42
|
+
def files() Git.files; end
|
43
|
+
|
44
|
+
|
45
|
+
### remote show origin|upstream|etc.
|
46
|
+
def remote() Git.remote; end
|
47
|
+
def origin() Git.origin; end
|
48
|
+
def upstream() Git.upstream; end
|
49
|
+
def origin?() Git.origin?; end
|
50
|
+
def upstream?() Git.upstream?; end
|
51
|
+
|
52
|
+
### branch management
|
53
|
+
def branch() Git.branch; end
|
54
|
+
def master?() Git.master?; end
|
55
|
+
def main?() Git.main?; end
|
56
|
+
|
57
|
+
|
58
|
+
def run( cmd ) Git::Shell.run( cmd ); end
|
59
|
+
end # class GitProject
|
60
|
+
end # module Gitti
|
61
|
+
|
@@ -0,0 +1,42 @@
|
|
1
|
+
|
2
|
+
module Gitti
|
3
|
+
|
4
|
+
|
5
|
+
class GitRepoSet ## todo: rename to Hash/Dict/List/Map or use GitHubRepoSet ??
|
6
|
+
|
7
|
+
def self.read( path )
|
8
|
+
txt = File.open( path, 'r:utf-8') { |f| f.read }
|
9
|
+
hash = YAML.load( txt )
|
10
|
+
new( hash )
|
11
|
+
end
|
12
|
+
|
13
|
+
|
14
|
+
def initialize( hash )
|
15
|
+
@hash = hash
|
16
|
+
end
|
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
|
+
|
23
|
+
def each
|
24
|
+
@hash.each do |org_with_counter,names|
|
25
|
+
|
26
|
+
## remove optional number from key e.g.
|
27
|
+
## mrhydescripts (3) => mrhydescripts
|
28
|
+
## footballjs (4) => footballjs
|
29
|
+
## etc.
|
30
|
+
|
31
|
+
org = org_with_counter.sub( /\([0-9]+\)/, '' ).strip
|
32
|
+
|
33
|
+
## puts " -- #{key_with_counter} [#{key}] --"
|
34
|
+
|
35
|
+
yield( org, names )
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
end ## class GitRepoSet
|
40
|
+
|
41
|
+
end ## module Gitti
|
42
|
+
|
data/lib/gitti/version.rb
CHANGED
@@ -1,9 +1,14 @@
|
|
1
|
-
# encoding: utf-8
|
2
1
|
|
3
|
-
module
|
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?
|
4
9
|
MAJOR = 0 ## todo: namespace inside version or something - why? why not??
|
5
|
-
MINOR =
|
6
|
-
PATCH =
|
10
|
+
MINOR = 4
|
11
|
+
PATCH = 2
|
7
12
|
VERSION = [MAJOR,MINOR,PATCH].join('.')
|
8
13
|
|
9
14
|
def self.version
|
@@ -17,5 +22,5 @@ module Gitti
|
|
17
22
|
def self.root
|
18
23
|
"#{File.expand_path( File.dirname(File.dirname(File.dirname(__FILE__))) )}"
|
19
24
|
end
|
20
|
-
end # module
|
25
|
+
end # module GittiCore
|
21
26
|
|
data/test/helper.rb
ADDED
data/test/test_base.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
###
|
2
|
+
# to run use
|
3
|
+
# ruby -I ./lib -I ./test test/test_base.rb
|
4
|
+
|
5
|
+
require 'helper'
|
6
|
+
|
7
|
+
class TestBase < MiniTest::Test
|
8
|
+
|
9
|
+
def test_branch
|
10
|
+
Git.branch
|
11
|
+
assert_equal true, Git.master?
|
12
|
+
assert_equal false, Git.main?
|
13
|
+
|
14
|
+
Git.remote
|
15
|
+
assert_equal true, Git.origin?
|
16
|
+
assert_equal false, Git.upstream?
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
def test_git_config
|
21
|
+
puts "---"
|
22
|
+
Git.config( 'user.name' )
|
23
|
+
Git.config( 'user.name', show_origin: true )
|
24
|
+
## Git.config( 'user.name', show_scope: true )
|
25
|
+
|
26
|
+
puts "---"
|
27
|
+
Git.config( /user/ ) ## note: pass in regex for regex match/search
|
28
|
+
Git.config( /user/, show_origin: true )
|
29
|
+
## Git.config( /user/, show_scope: true )
|
30
|
+
|
31
|
+
puts "---"
|
32
|
+
Git.config( /user\./ ) ## note: pass in regex for regex match/search
|
33
|
+
|
34
|
+
puts "---"
|
35
|
+
## note: if NOT found Git.config will exit(1) !!!
|
36
|
+
## Git.config( /proxy/, show_origin: true )
|
37
|
+
## Git.config( /http/, show_origin: true )
|
38
|
+
|
39
|
+
puts "---"
|
40
|
+
end
|
41
|
+
|
42
|
+
end # class TestBase
|
43
|
+
|
metadata
CHANGED
@@ -1,29 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gitti
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2
|
4
|
+
version: 0.4.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gerald Bauer
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-09-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: logutils
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - ">="
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: '0'
|
20
|
-
type: :runtime
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - ">="
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: '0'
|
27
13
|
- !ruby/object:Gem::Dependency
|
28
14
|
name: rdoc
|
29
15
|
requirement: !ruby/object:Gem::Requirement
|
@@ -44,32 +30,37 @@ dependencies:
|
|
44
30
|
requirements:
|
45
31
|
- - "~>"
|
46
32
|
- !ruby/object:Gem::Version
|
47
|
-
version: '3.
|
33
|
+
version: '3.16'
|
48
34
|
type: :development
|
49
35
|
prerelease: false
|
50
36
|
version_requirements: !ruby/object:Gem::Requirement
|
51
37
|
requirements:
|
52
38
|
- - "~>"
|
53
39
|
- !ruby/object:Gem::Version
|
54
|
-
version: '3.
|
55
|
-
description: gitti - (yet) another (lite) git command line
|
40
|
+
version: '3.16'
|
41
|
+
description: gitti - (yet) another (lite) git command line helper / wrapper
|
56
42
|
email: ruby-talk@ruby-lang.org
|
57
43
|
executables: []
|
58
44
|
extensions: []
|
59
45
|
extra_rdoc_files:
|
60
|
-
-
|
46
|
+
- CHANGELOG.md
|
61
47
|
- Manifest.txt
|
62
48
|
- README.md
|
63
49
|
files:
|
64
|
-
-
|
50
|
+
- CHANGELOG.md
|
65
51
|
- Manifest.txt
|
66
52
|
- README.md
|
67
53
|
- Rakefile
|
68
54
|
- lib/gitti.rb
|
69
|
-
- lib/gitti/
|
70
|
-
- lib/gitti/
|
55
|
+
- lib/gitti/base.rb
|
56
|
+
- lib/gitti/git.rb
|
57
|
+
- lib/gitti/mirror.rb
|
58
|
+
- lib/gitti/project.rb
|
59
|
+
- lib/gitti/reposet.rb
|
71
60
|
- lib/gitti/version.rb
|
72
|
-
|
61
|
+
- test/helper.rb
|
62
|
+
- test/test_base.rb
|
63
|
+
homepage: https://github.com/rubycoco/gitti
|
73
64
|
licenses:
|
74
65
|
- Public Domain
|
75
66
|
metadata: {}
|
@@ -83,7 +74,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
83
74
|
requirements:
|
84
75
|
- - ">="
|
85
76
|
- !ruby/object:Gem::Version
|
86
|
-
version:
|
77
|
+
version: 2.2.2
|
87
78
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
88
79
|
requirements:
|
89
80
|
- - ">="
|
@@ -91,8 +82,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
91
82
|
version: '0'
|
92
83
|
requirements: []
|
93
84
|
rubyforge_project:
|
94
|
-
rubygems_version: 2.2
|
85
|
+
rubygems_version: 2.5.2
|
95
86
|
signing_key:
|
96
87
|
specification_version: 4
|
97
|
-
summary: gitti - (yet) another (lite) git command line
|
88
|
+
summary: gitti - (yet) another (lite) git command line helper / wrapper
|
98
89
|
test_files: []
|
data/lib/gitti/lib.rb
DELETED
@@ -1,83 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
|
4
|
-
module Gitti
|
5
|
-
|
6
|
-
class GitError < StandardError
|
7
|
-
end
|
8
|
-
|
9
|
-
class GitLib
|
10
|
-
|
11
|
-
def clone( repo, opts={} )
|
12
|
-
command( "clone #{repo}" )
|
13
|
-
end
|
14
|
-
|
15
|
-
def mirror( repo, opts={} )
|
16
|
-
command( "clone --mirror #{repo}" )
|
17
|
-
end
|
18
|
-
|
19
|
-
def pull( opts={} )
|
20
|
-
command( "pull" )
|
21
|
-
end
|
22
|
-
|
23
|
-
def remote_update( opts={} )
|
24
|
-
command( "remote update" )
|
25
|
-
end
|
26
|
-
|
27
|
-
|
28
|
-
## todo/fix:
|
29
|
-
## add last_exit or something ?? why? why not??
|
30
|
-
|
31
|
-
def command( cmd )
|
32
|
-
## note: for now use Kernel#system for calling external git command
|
33
|
-
##
|
34
|
-
|
35
|
-
cmdline = "git #{cmd}"
|
36
|
-
puts " trying >#{cmdline}< in (#{Dir.pwd})..."
|
37
|
-
|
38
|
-
result = nil
|
39
|
-
result = system( cmdline )
|
40
|
-
|
41
|
-
pp result
|
42
|
-
|
43
|
-
# note: Kernel#system returns
|
44
|
-
# - true if the command gives zero exit status
|
45
|
-
# - false for non zero exit status
|
46
|
-
# - nil if command execution fails
|
47
|
-
# An error status is available in $?.
|
48
|
-
|
49
|
-
if result.nil?
|
50
|
-
puts "*** error was #{$?}"
|
51
|
-
fail "[Kernel.system] command execution failed >#{cmdline}< - #{$?}"
|
52
|
-
elsif result ## true => zero exit code (OK)
|
53
|
-
puts 'OK' ## zero exit; assume OK
|
54
|
-
true ## assume ok
|
55
|
-
else ## false => non-zero exit code (ERR/NOK)
|
56
|
-
puts "*** error: non-zero exit - #{$?} !!" ## non-zero exit (e.g. 1,2,3,etc.); assume error
|
57
|
-
|
58
|
-
## log error for now ???
|
59
|
-
# File.open( './errors.log', 'a' ) do |f|
|
60
|
-
# f.write "#{Time.now} -- repo #{@owner}/#{@name} - command execution failed - non-zero exit\n"
|
61
|
-
# end
|
62
|
-
raise GitError.new( "command execution failed >#{cmdline}< - non-zero exit (#{$?})" )
|
63
|
-
end
|
64
|
-
end # method command
|
65
|
-
end # class Lib
|
66
|
-
|
67
|
-
|
68
|
-
module Git
|
69
|
-
## todo/fix: use "shared" singelton lib - why? why not??
|
70
|
-
def self.clone( repo, opts={} ) GitLib.new.clone( repo, opts ); end
|
71
|
-
def self.mirror( repo, opts={} ) GitLib.new.mirror( repo, opts ); end
|
72
|
-
|
73
|
-
def self.pull( opts={} ) GitLib.new.pull( opts ); end
|
74
|
-
def self.remote_update( opts={} ) GitLib.new.remote_update( opts ); end
|
75
|
-
end # module Git
|
76
|
-
|
77
|
-
end # module Gitti
|
78
|
-
|
79
|
-
|
80
|
-
### convenience top level Git module - check if defined? make optional? why? why not??
|
81
|
-
## Git = Gitti::Git
|
82
|
-
|
83
|
-
# for now use include Gitti - why? why not??
|
@@ -1,37 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
module Gitti
|
4
|
-
|
5
|
-
|
6
|
-
class GitRepoSet ## todo: rename to Hash/Dict/List/Map or use GitHubRepoSet ??
|
7
|
-
|
8
|
-
def self.from_file( path )
|
9
|
-
hash = YAML.load_file( path )
|
10
|
-
self.new( hash )
|
11
|
-
end
|
12
|
-
|
13
|
-
|
14
|
-
def initialize( hash )
|
15
|
-
@hash = hash
|
16
|
-
end
|
17
|
-
|
18
|
-
def each
|
19
|
-
@hash.each do |key_with_counter,values|
|
20
|
-
|
21
|
-
## remove optional number from key e.g.
|
22
|
-
## mrhydescripts (3) => mrhydescripts
|
23
|
-
## footballjs (4) => footballjs
|
24
|
-
## etc.
|
25
|
-
|
26
|
-
key = key_with_counter.sub( /\s+\([0-9]+\)/, '' )
|
27
|
-
|
28
|
-
puts " -- #{key_with_counter} [#{key}] --"
|
29
|
-
|
30
|
-
yield( key, values )
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
end ## class GitRepoSet
|
35
|
-
|
36
|
-
end ## module Gitti
|
37
|
-
|