git_commands 3.4.0 → 3.5.1

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: c4e5ee0c7fb792943323a8df9f026e46ed043c2e
4
- data.tar.gz: a1d6e9c1709de3a742c9c20aa22cfbd6bdc86290
3
+ metadata.gz: 172e8dcb4ab65fc7f43efc68e42709b8730fb54b
4
+ data.tar.gz: 8a9fa030b351e83b13e3dd4ea7a5c0684670a05d
5
5
  SHA512:
6
- metadata.gz: 725df66a77dd900e4f2c2aa5543c1e25ffc24d62cc332bc9e8fb7d7d29123f0eac821a57b8912d56c039eebcc1135f3ae1187531cddcfeb10c8f204e87854d53
7
- data.tar.gz: 2b3162474ae63758828d6c1e9dc071c813a92565200e7a69d91c4808507718f11b62a05ab6598da26035e7c4fc95c3c6db38e6724f2d2c5ffda8b9a899836042
6
+ metadata.gz: 0adf55993c9add401986a9d019e712998813b087bcbbb27bcb339daaddab77a9c2ba3ddb8af7191c884462b6f7c05e55591323b7cd30dd96c5f4679cf1d7d070
7
+ data.tar.gz: 5222cf662d765a7fd08cbd31bb357abe849f2b5c42974c599a1b5e1462b4cb065720c58b5f6eb0a4d74298af3e770bf87efd97a7a7f35092be5b59084906fa3e
data/README.md CHANGED
@@ -16,9 +16,8 @@
16
16
  ## Workflow
17
17
  This script facilitates adopting a subset of the branch-featuring workflow characterised by:
18
18
  * each feature will have its own branch
19
- * feature branches derive directly form master
20
- * integration of master to feature branch happens via rebasing to maintain a straight commits line
21
- * force pushing of feature branches to origin is not an issue
19
+ * integration of feature branch with defaukt one happens via rebasing to maintain a straight commits line
20
+ * is not an issue to `force-with-lease` push feature pranch to origin
22
21
  * release branches are created aggregating multiple branches
23
22
 
24
23
  ## Scope
@@ -48,10 +47,11 @@ Display the help of a specific command by:
48
47
 
49
48
  ```
50
49
  rebase --help
51
- Usage: rebase --repo=/Users/Elvis/greatest_hits --branches=feature/love_me_tender,fetaure/teddybear
50
+ Usage: rebase --repo=/Users/Elvis/greatest_hits --origin=upstream --default=production --branches=feature/love_me_tender,fetaure/teddybear
52
51
  -r, --repo=REPO The path to the existing GIT repository
52
+ -o, --origin=ORIGIN Specify the remote alias (origin)
53
+ -d, --default=DEFAULT Specify the default branch (master)
53
54
  -b, --branches=BRANCHES Specify branches as: 1. a comma-separated list of names 2. the path to a file containing names on each line 3. via pattern matching
54
- -t, --target=TARGET Specify the target branch, default to master
55
55
  -h, --help Prints this help
56
56
  ```
57
57
 
@@ -133,8 +133,8 @@ rebase --repo=/Users/Elvis/greatest_hits --branches=noent1,noent2
133
133
  No branches loaded!
134
134
  ```
135
135
 
136
- ##### Master branch
137
- Master branch cannot be included into the branches list for obvious reasons:
136
+ ##### Default branch
137
+ Default branch cannot be included into the branches list for obvious reasons:
138
138
 
139
139
  ```
140
140
  rebase --repo=/Users/Elvis/greatest_hits --branches=master,feature/love_me_tender
@@ -148,7 +148,7 @@ Successfully loaded 1 branch:
148
148
  Here are the available GIT commands:
149
149
 
150
150
  #### Rebase
151
- This command is useful in case you have several branches to rebase with _origin/master_ (or another specified target) frequently.
151
+ This command is useful in case you have to rebase several branches with _origin/master_ (or another specified default) frequently.
152
152
  A confirmation is asked to before rebasing.
153
153
 
154
154
  ```
@@ -156,17 +156,17 @@ rebase --repo=/Users/Elvis/greatest_hits --branches=feature/love_me_tender,featu
156
156
  ...
157
157
  ```
158
158
 
159
- ##### Target branch
160
- The rebasing runs considering master branch as the target one.
161
- In case you need to rebase against a different target branch you can specify it:
159
+ ##### Changing origin
160
+ The rebasing runs considering `master` branch as the default one and `origin` as the remote alias.
161
+ In case you need to rebase against a different origin and default branch you can specify them by command line:
162
162
  ```
163
- rebase --repo=/Users/Elvis/greatest_hits --target=rc/release_to_graceland --branches=feature/love_me_tender
163
+ rebase --repo=/Users/Elvis/greatest_hits --origin=upstream --default=production --branches=feature/love_me_tender
164
164
 
165
165
  Loading branches file...
166
166
  Successfully loaded 1 branch:
167
167
  01. feature/love_me_tender
168
168
 
169
- Proceed rebasing these branches with rc/release_to_graceland (Y/N)?
169
+ Proceed rebasing these branches with upstream/production (Y/N)?
170
170
  ```
171
171
 
172
172
  #### Remove
@@ -180,10 +180,45 @@ remove --repo=/temp/top_20 --branches=*obsolete*
180
180
 
181
181
  #### Aggregate
182
182
  This command aggregates all of the specified branches into a single one in case you want to create a release branch.
183
- The created aggregate branch follows this naming convention: *aggregate/yyyy_mm_dd*
184
183
  A confirmation is asked before aggregating.
185
184
 
186
185
  ```
187
186
  aggregate --repo=/Users/Elvis/greatest_hits --branches=*ready*
188
187
  ...
189
188
  ```
189
+
190
+ ##### Aggregate naming
191
+ The created aggregate branch follows a default naming convention pattern:
192
+ `aggregate/<timestamp>`
193
+
194
+ Each of the term within the `<` and `>` chars are replaced by related (upcased) environment variables, but for the `timestamp`, that is computed at runtime in the `yyyymmdd` format.
195
+ Consider a valid pattern should at least contain one replaceable part within the `<` and `>` chars.
196
+
197
+ You can overwrite the naming pattern by specifying the following environment variables:
198
+ * `AGGREGATE_NAME` - the name to be used directly for the aggregator branch, without any pattern replacements
199
+ * `AGGREGATE_PATTERN` - change the default pattern by specifying the related environment variables for each parts within the `<` and `>` chars
200
+
201
+ ###### Examples
202
+ Passing directly the aggregate name:
203
+ ```shell
204
+ AGGREGATE_NAME=my_aggregate aggregate --repo=/Users/Elvis/greatest_hits --branches=*ready*
205
+ ...
206
+ Aggregate branches into my_aggregate (Y/N)?
207
+ ```
208
+
209
+ Using the default pattern:
210
+ ```shell
211
+ aggregate --repo=/Users/Elvis/greatest_hits --branches=*ready*
212
+ ...
213
+ Aggregate branches into release/20170307 (Y/N)?
214
+ ```
215
+
216
+ Using a custom pattern:
217
+ ```shell
218
+ RELEASE_TYPE=bugfix \
219
+ RISK=HIGH \
220
+ PROGRESSIVE=3 \
221
+ AGGREGATE_PATTERN="release/rc-<progressive>.<release_type>_<risk>_<timestamp>" aggregate --repo=/Users/Elvis/greatest_hits --branches=*ready*
222
+ ...
223
+ Aggregate branches into release/rc-3.bugfix_HIGH_20170307 (Y/N)?
224
+ ```
@@ -0,0 +1,43 @@
1
+ module GitCommands
2
+ class Aggregator
3
+ PATTERN = ENV.fetch("AGGREGATE_PATTERN") { "release/<timestamp>" }
4
+ NAME = ENV["AGGREGATE_NAME"]
5
+
6
+ class InvalidPatternError < ArgumentError; end
7
+
8
+ def initialize(name: NAME, pattern: PATTERN)
9
+ @name = name
10
+ @pattern = check_pattern(pattern)
11
+ define_methods
12
+ end
13
+
14
+ def timestamp
15
+ @timestamp ||= Time.new.strftime("%Y%m%d")
16
+ end
17
+
18
+ def call
19
+ return @name if @name
20
+ @pattern.gsub(/<[\w_]+>/) do |part|
21
+ msg = part.gsub(/<|>/, "")
22
+ send(msg)
23
+ end
24
+ end
25
+
26
+ private def check_pattern(pattern)
27
+ fail InvalidPatternError unless pattern.match(/<\w+>/)
28
+ pattern
29
+ end
30
+
31
+ private def pattern_methods
32
+ @methods ||= @pattern.scan(/<(\w+)>/).flatten - ["timestamp"]
33
+ end
34
+
35
+ def define_methods
36
+ pattern_methods.each do |name|
37
+ define_singleton_method(name) do
38
+ ENV.fetch(name.upcase) { "" }
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -3,11 +3,11 @@ require "pathname"
3
3
  module GitCommands
4
4
  using Colorize
5
5
  class Branch
6
- MASTER = "master"
7
- ORIGIN = "origin/"
6
+ DEFAULT = "master"
7
+ ORIGIN = "origin"
8
8
 
9
9
  def self.strip_origin(name)
10
- name.strip.split(ORIGIN).last
10
+ name.strip.split("#{ORIGIN}/").last
11
11
  end
12
12
 
13
13
  def self.by_file(path)
@@ -19,9 +19,9 @@ module GitCommands
19
19
 
20
20
  def self.by_pattern(pattern)
21
21
  return [] unless pattern.index("*")
22
- `git branch -r --list #{ORIGIN}#{pattern}`.split("\n").map do |name|
22
+ `git branch -r --list #{ORIGIN}/#{pattern}`.split("\n").map do |name|
23
23
  new(strip_origin(name))
24
- end.reject(&:master?)
24
+ end.reject(&:default?)
25
25
  end
26
26
 
27
27
  def self.by_names(names_list)
@@ -54,7 +54,7 @@ module GitCommands
54
54
  end
55
55
 
56
56
  def valid?
57
- return false if master?
57
+ return false if default?
58
58
  return false unless exists?
59
59
  true
60
60
  end
@@ -63,12 +63,12 @@ module GitCommands
63
63
  self.name == other.name
64
64
  end
65
65
 
66
- def master?
67
- @name == MASTER
66
+ def default?
67
+ @name == DEFAULT
68
68
  end
69
69
 
70
70
  def exists?(remote = true)
71
- origin = ORIGIN if remote
71
+ origin = "#{ORIGIN}/" if remote
72
72
  `git rev-parse --verify #{origin}#{@name} 2> /dev/null`.match(/^[0-9a-z]+/)
73
73
  end
74
74
  end
@@ -20,15 +20,15 @@ module GitCommands
20
20
 
21
21
  def call
22
22
  parser.parse!(@args)
23
- computer = @computer_klass.new(repo: @repo, branches: @branches, target: target)
23
+ computer = @computer_klass.new(repo: @repo, branches: @branches, origin: @origin, default: default)
24
24
  computer.send(@command_name)
25
25
  rescue Repository::PathError, Computer::GitError, AbortError, Repository::InvalidError => e
26
26
  error(e.message)
27
27
  exit
28
28
  end
29
29
 
30
- private def target
31
- @target || Branch::MASTER
30
+ private def default
31
+ @default || Branch::DEFAULT
32
32
  end
33
33
 
34
34
  private def check_command_name(name)
@@ -38,18 +38,22 @@ module GitCommands
38
38
 
39
39
  private def parser
40
40
  OptionParser.new do |opts|
41
- opts.banner = "Usage: #{@command_name} --repo=/Users/Elvis/greatest_hits --branches=feature/love_me_tender,fetaure/teddybear"
41
+ opts.banner = "Usage: #{@command_name} --repo=/Users/Elvis/greatest_hits --origin=upstream --default=production --branches=feature/love_me_tender,fetaure/teddybear"
42
42
 
43
43
  opts.on("-rREPO", "--repo=REPO", "The path to the existing GIT repository") do |repo|
44
44
  @repo = repo
45
45
  end
46
46
 
47
- opts.on("-bBRANCHES", "--branches=BRANCHES", "Specify branches as: 1. a comma-separated list of names 2. the path to a file containing names on each line 3. via pattern matching") do |branches|
48
- @branches = branches
47
+ opts.on("-oORIGIN", "--origin=ORIGIN", "Specify the remote alias (origin)") do |origin|
48
+ @origin = origin
49
+ end
50
+
51
+ opts.on("-dDEFAULT", "--default=DEFAULT", "Specify the default branch (master)") do |default|
52
+ @default = default
49
53
  end
50
54
 
51
- opts.on("-tTARGET", "--target=TARGET", "Specify the target branch, default to master") do |target|
52
- @target = target
55
+ opts.on("-bBRANCHES", "--branches=BRANCHES", "Specify branches as: 1. a comma-separated list of names 2. the path to a file containing names on each line 3. via pattern matching") do |branches|
56
+ @branches = branches
53
57
  end
54
58
 
55
59
  opts.on("-h", "--help", "Prints this help") do
@@ -1,6 +1,7 @@
1
1
  require "git_commands/prompt"
2
2
  require "git_commands/branch"
3
3
  require "git_commands/repository"
4
+ require "git_commands/aggregator"
4
5
 
5
6
  module GitCommands
6
7
  class Computer
@@ -9,15 +10,14 @@ module GitCommands
9
10
  class GitError < StandardError; end
10
11
 
11
12
  attr_reader :out
12
- attr_accessor :target
13
13
 
14
- def initialize(repo:, branches:, target: Branch::MASTER, repo_klass: Repository, branch_klass: Branch, out: STDOUT)
14
+ def initialize(repo:, branches:, origin: Branch::ORIGIN, default: Branch::DEFAULT, repo_klass: Repository, branch_klass: Branch, out: STDOUT)
15
15
  @out = out
16
16
  @repo = repo_klass.new(repo)
17
- @target = target
17
+ @origin = origin
18
+ @default = default
18
19
  Dir.chdir(@repo) do
19
20
  @branches = branch_klass.factory(branches)
20
- @timestamp = Time.new.strftime("%Y-%m-%d")
21
21
  print_branches
22
22
  end
23
23
  end
@@ -35,14 +35,14 @@ module GitCommands
35
35
  end
36
36
 
37
37
  def rebase
38
- confirm("Proceed rebasing these branches with #{@target}") do
38
+ confirm("Proceed rebasing these branches with: #{local_def}") do
39
39
  enter_repo do
40
40
  @branches.each do |branch|
41
41
  warning("Rebasing branch: #{branch}")
42
42
  `git checkout #{branch}`
43
- `git pull origin #{branch}`
43
+ `git pull -r origin #{branch}`
44
44
  next unless rebase_with
45
- `git push -f origin #{branch}`
45
+ `git push --force-with-lease origin #{branch}`
46
46
  success("Rebased successfully!")
47
47
  end
48
48
  remove_locals
@@ -50,23 +50,23 @@ module GitCommands
50
50
  end
51
51
  end
52
52
 
53
- def aggregate
54
- temp = "temp/#{@timestamp}"
55
- target = "aggregate/#{@timestamp}"
56
- confirm("Aggregate branches into #{target}") do
53
+ def aggregate(aggregator = Aggregator.new)
54
+ temp = "temp/#{aggregator.timestamp}"
55
+ aggregate_name = aggregator.call
56
+ confirm("Aggregate branches into #{aggregate_name}") do
57
57
  enter_repo do
58
- `git branch #{target}`
58
+ `git branch #{aggregate_name}`
59
59
  @branches.each do |branch|
60
60
  warning("Merging branch: #{branch}")
61
61
  `git checkout -b #{temp} origin/#{branch} --no-track`
62
- clean_and_exit([temp, target]) unless rebase_with
63
- clean_and_exit([temp]) unless rebase_with(target)
64
- `git checkout #{target}`
62
+ clean_and_exit([temp, aggregate_name]) unless rebase_with
63
+ clean_and_exit([temp]) unless rebase_with(aggregate_name)
64
+ `git checkout #{aggregate_name}`
65
65
  `git merge #{temp}`
66
66
  `git branch -D #{temp}`
67
67
  end
68
68
  end
69
- success("#{target} branch created")
69
+ success("#{aggregate_name} branch created")
70
70
  end
71
71
  end
72
72
 
@@ -79,11 +79,13 @@ module GitCommands
79
79
  end
80
80
 
81
81
  private def align
82
- `git checkout #{@target}`
83
- `git pull`
82
+ `git fetch #{@origin} #{@default}`
83
+ `git fetch origin`
84
+ `git checkout #{@default}`
85
+ `git pull -r`
84
86
  end
85
87
 
86
- private def rebase_with(branch = "#{Branch::ORIGIN}#{@target}")
88
+ private def rebase_with(branch = local_def)
87
89
  `git rebase #{branch}`
88
90
  return true unless @repo.locked?
89
91
  @repo.unlock
@@ -98,7 +100,7 @@ module GitCommands
98
100
  end
99
101
 
100
102
  private def remove_locals(branches = @branches)
101
- `git checkout #{@target}`
103
+ `git checkout #{@default}`
102
104
  branches.each do |branch|
103
105
  `git branch -D #{branch}`
104
106
  end
@@ -108,5 +110,9 @@ module GitCommands
108
110
  remove_locals(branches)
109
111
  exit
110
112
  end
113
+
114
+ private def local_def
115
+ "#{@origin}/#{@default}"
116
+ end
111
117
  end
112
118
  end
@@ -16,7 +16,7 @@ module GitCommands
16
16
  end
17
17
 
18
18
  def success(message)
19
- out.puts message.to_s.green
19
+ out.puts "\n#{message}".green
20
20
  true
21
21
  end
22
22
 
@@ -1,3 +1,3 @@
1
1
  module GitCommands
2
- VERSION = "3.4.0"
2
+ VERSION = "3.5.1"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: git_commands
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.4.0
4
+ version: 3.5.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - costajob
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-02-27 00:00:00.000000000 Z
11
+ date: 2017-03-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -75,6 +75,7 @@ files:
75
75
  - bin/setup
76
76
  - git_commands.gemspec
77
77
  - lib/git_commands.rb
78
+ - lib/git_commands/aggregator.rb
78
79
  - lib/git_commands/branch.rb
79
80
  - lib/git_commands/cli.rb
80
81
  - lib/git_commands/colorize.rb