git_curate 1.0.1 → 1.2.0.beta

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
  SHA256:
3
- metadata.gz: 32a05c73884f0ccb28a0c77fbecce6aa123c866b8de6a519fd1dc02f5ae81475
4
- data.tar.gz: fa8f0967a63ceb77a4cd325d47e9306882f1de712d656b63ccb6936386ecbc6d
3
+ metadata.gz: 4460f5b9f1e2e1dc37bc0afbab3b0d67ab3a1a767d66c2e41c49a31de9ae8ceb
4
+ data.tar.gz: 64d28c60264cde31f088074f7d431ae8019f97fa0117b940c05cc9d40d855187
5
5
  SHA512:
6
- metadata.gz: a229f824eabb501d718b0533041c7308536fb939219391544b6cca176e5c278e52a0951d14d897142737226175e1911057c7306b430ca0e8752b2b6c920b8e38
7
- data.tar.gz: e9166c2594376faeb422fbb4936d788249b59d5b6af530edd655c5f24d9be566615326337b490fb3185df50e1d1d0753bf8bf86d7bb6e55d0521409f43557520
6
+ metadata.gz: 6ffe2949a193e80a8fda6c480464eb73ac4f859309f2deb1f48c818f916566eac2a3b5f4fd6e97b031df6b673f79fc59df03262d44d934c754d3f1b45efa9d15
7
+ data.tar.gz: 3941f4ed634a97b83db6d13d91288e37271e61d8a1f9c73c2e88e492b9805c417f261c0bb9af49a9a8c6f7dd25759bb287f3adee5c907287041e65b87e84fb20
data/.travis.yml CHANGED
@@ -1,7 +1,7 @@
1
- sudo: false
2
1
  language: ruby
3
2
  rvm:
4
- - 2.4.9
3
+ - 2.4.10
5
4
  - 2.5.8
6
5
  - 2.6.6
7
- - 2.7.1
6
+ - 2.7.2
7
+ - 3.0.0
data/CHANGELOG.md CHANGED
@@ -1,5 +1,39 @@
1
1
  # Changelog
2
2
 
3
+ ### v1.2.0.beta
4
+
5
+ * Fix for issue #16: "undefined method upstream" error on MacOS 15.7
6
+ * Add rugged library
7
+
8
+ ### v1.1.2
9
+
10
+ * Dependency version upgrades
11
+ * Include Ruby v3 in automated tests
12
+
13
+ ### v1.1.1
14
+
15
+ * Dependency version upgrades
16
+
17
+ ### v1.1.0
18
+
19
+ #### Change that may be breaking for an obscure use case
20
+
21
+ * Output more helpful message in case there are no deletable branches when `git curate` run without `-l`/`--list` flag.
22
+
23
+ This will be a breaking change but only in the unlikely event that the output of `git curate` is being piped
24
+ through or processed by another program when run in _interactive_ (non-`--list`) mode.
25
+
26
+ _New behaviour when there are no deletable branches:_
27
+ Outputs `There are no local branches that can be deleted.`
28
+
29
+ _Old behaviour when there are no deletable branches:_
30
+ Outputs (irrelevantly and confusingly) the legend of interactive commands, followed by the message
31
+ `No branches deleted.`
32
+
33
+ ### v1.0.2
34
+
35
+ * Fix incorrect status-vs-upstream when commit subject begins with square-bracket-enclosed string.
36
+
3
37
  ### v1.0.1
4
38
 
5
39
  * Fix `fatal: bad revision '+'` error on encountering multiple worktrees.
data/Gemfile CHANGED
@@ -4,3 +4,5 @@ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
4
4
 
5
5
  # Specify your gem's dependencies in git_curate.gemspec
6
6
  gemspec
7
+
8
+ gem "rugged", "~> 1.1"
data/README.md CHANGED
@@ -11,7 +11,7 @@
11
11
 
12
12
  After a while, my local repo becomes cluttered with branches, and `git branch` outputs an awkwardly
13
13
  long list. I want to delete some of those branches to bring that list back under control; but I
14
- can't always remember which branches I want to keep from the branch names alone; and inspecting them
14
+ can’t always remember which branches I want to keep from the branch names alone; and inspecting them
15
15
  one at a time and _then_ running `git branch -D` in a separate step, is painful.
16
16
 
17
17
  `git curate` is intended to ease this pain. It steps you through the local branches of a repo one at
@@ -27,12 +27,12 @@ a time, outputting the following information about each:
27
27
  You can then select whether to delete or keep each branch as you go.
28
28
 
29
29
  **NOTE** `git curate` does _not_ run `git fetch` prior to generating its output. If you want to
30
- be sure that the "Status vs upstream" column reflects the latest state of the upstream branches
30
+ be sure that the “Status vs upstream” column reflects the latest state of the upstream branches
31
31
  as per their remote repository, you should run `git fetch` first.
32
32
 
33
33
  ## Installation
34
34
 
35
- You'll need Ruby (v2.4.9 or greater) installed. Run:
35
+ You’ll need Ruby (v2.4.9 or greater) installed. Run:
36
36
 
37
37
  ```
38
38
  gem install git_curate
@@ -51,15 +51,16 @@ git curate
51
51
  This will step you through your local branches one at a time, outputting some information about
52
52
  each, and asking you whether to keep or delete each branch.
53
53
 
54
- At each branch, enter "k"—or simply press Enter—to _keep_ the branch and move to the next one;
55
- or enter "d" to select the branch for deletion.
54
+ At each branch, enter “k”—or simply press Enter—to _keep_ the branch and move to the next one;
55
+ or enter “d” to select the branch for deletion.
56
56
 
57
- Entering "e" will end the session immediately, deleting all selected branches; and "a" will
57
+ Entering “e” will end the session immediately, deleting all selected branches; and “a” will
58
58
  abort the session without deleting any branches. Once the final branch has been considered,
59
59
  any selected branches will be immediately deleted.
60
60
 
61
61
  Note the branch you are currently on will not be included in the list, as `git` does not allow you to delete
62
- the branch you're on.
62
+ the branch you’re on. (The same applies to any branches that are currently checked out in other
63
+ [worktrees](https://git-scm.com/docs/git-worktree).)
63
64
 
64
65
  If you just want to view the information about your local branches without stepping through
65
66
  them interactively, enter `git curate --list` or `git curate -l`. Your current branch _will_
@@ -76,8 +77,7 @@ To run the test suite, run `bundle exec rake spec`. For a list of other Rake tas
76
77
 
77
78
  ## License
78
79
 
79
- The gem is available as open source under the terms of the [MIT
80
- License](http://opensource.org/licenses/MIT).
80
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
81
81
 
82
82
  [Gem Version]: https://rubygems.org/gems/git_curate
83
83
  [Build Status]: https://travis-ci.org/matt-harvey/git_curate
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.0.1
1
+ 1.2.0.beta
data/git_curate.gemspec CHANGED
@@ -29,8 +29,8 @@ Gem::Specification.new do |spec|
29
29
  spec.require_paths = ["lib"]
30
30
 
31
31
  spec.add_runtime_dependency "highline", "2.0.3"
32
- spec.add_runtime_dependency "tabulo", "2.5.0"
33
- spec.add_runtime_dependency "tty-screen", "0.7.1"
32
+ spec.add_runtime_dependency "tabulo", "2.6.2"
33
+ spec.add_runtime_dependency "tty-screen", "0.8.1"
34
34
 
35
35
  spec.add_development_dependency "bundler"
36
36
  spec.add_development_dependency "coveralls"
@@ -1,18 +1,19 @@
1
+ require "rugged"
2
+
1
3
  module GitCurate
2
4
 
5
+ UpstreamInfo = Struct.new(:upstream, :status)
6
+
3
7
  class Branch
4
8
 
9
+ @@repo = Rugged::Repository.new(".")
10
+
5
11
  # Regex for determining whether a "raw" branch name is the name of the current branch
6
12
  # on this or another worktree.
7
13
  CURRENT_BRANCH_REGEX = /^[+*]\s+/
8
14
 
9
- # Regexes for unpacking the output of `git branch -vv`
10
- BRANCH_NAME_REGEX = /\s+/
11
- LEADING_STAR_REGEX = /^\* /
12
- LEADING_PLUS_REGEX = /^\+ /
13
- REMOTE_INFO_REGEX = /^[^\s]+\s+[^\s]+\s+(\(.+\)\s+)?\[(?<remote_info>.+)\]/
14
-
15
- # Returns the branch name, with "* " prefixed if it's the current branch.
15
+ # Returns the branch name, with "* " prefixed if it's the current branch on the current
16
+ # worktree, or "+ " if it's the current branch on another worktree.
16
17
  attr_reader :raw_name
17
18
 
18
19
  # Returns a human-friendly string describing the status of the branch relative to the upstream branch
@@ -24,19 +25,8 @@ module GitCurate
24
25
  @proper_name ||= @raw_name.lstrip.sub(CURRENT_BRANCH_REGEX, '')
25
26
  end
26
27
 
27
- # Returns truthy if and only if this is the currently checked out branch on the current
28
- # worktree.
29
- def current_branch_this_worktree?
30
- @current_branch_this_worktree ||= (@raw_name =~ LEADING_STAR_REGEX)
31
- end
32
-
33
- # Returns truthy if and only if this is the currently checked out branch on another worktree.
34
- def current_branch_other_worktree?
35
- @current_branch_other_worktree ||= (@raw_name =~ LEADING_PLUS_REGEX)
36
- end
37
-
38
28
  def current?
39
- current_branch_this_worktree? || current_branch_other_worktree?
29
+ @current ||= (@raw_name =~ CURRENT_BRANCH_REGEX)
40
30
  end
41
31
 
42
32
  # Return truthy if and only if this branch has been merged into the current HEAD.
@@ -79,43 +69,43 @@ module GitCurate
79
69
 
80
70
  # Returns the local branches
81
71
  def self.local
82
- merged_branch_raw_names = Util.command_to_a("git branch --merged").to_set
72
+ rugged_branches = @@repo.branches
73
+ repo_head_target = @@repo.head.target
74
+
75
+ Util.command_to_a("git branch").map do |line|
76
+ raw_branch_name = line.strip
77
+ proper_branch_name = raw_branch_name.gsub(CURRENT_BRANCH_REGEX, "")
78
+ rugged_branch = rugged_branches[proper_branch_name]
79
+ upstream = rugged_branch.upstream
80
+ upstream_data =
81
+ if upstream
82
+ target_id = rugged_branch.target_id
83
+ ahead, behind = @@repo.ahead_behind(target_id, upstream.target_id)
84
+ parts = []
85
+ parts << "ahead #{ahead}" if ahead != 0
86
+ parts << "behind #{behind}" if behind != 0
87
+ if parts.any?
88
+ parts.join(", ").capitalize
89
+ else
90
+ "Up to date"
91
+ end
92
+ else
93
+ "No upstream"
94
+ end
95
+
96
+ target = rugged_branch.resolve.target
97
+ merged = (@@repo.merge_base(repo_head_target, target) == target.oid)
83
98
 
84
- branch_info.map do |raw_name, info|
85
- new(raw_name, merged: merged_branch_raw_names.include?(raw_name), upstream_info: info)
99
+ new(
100
+ raw_branch_name,
101
+ merged: merged,
102
+ upstream_info: upstream_data,
103
+ )
86
104
  end
87
105
  end
88
106
 
89
107
  private
90
108
 
91
- # Returns a Hash containing, as keys, the raw names of all local branches and, as values,
92
- # a brief description of each branch's status relative to its upstream branch (up to
93
- # date, or ahead/behind).
94
- def self.branch_info
95
- Util.command_to_a("git branch -vv").map do |line|
96
- line_is_current_branch = (line =~ CURRENT_BRANCH_REGEX)
97
- tidied_line = (line_is_current_branch ? line.gsub(CURRENT_BRANCH_REGEX, "") : line)
98
- proper_branch_name = tidied_line.split(BRANCH_NAME_REGEX)[0]
99
- raw_branch_name =
100
- if line =~ LEADING_STAR_REGEX
101
- "* #{proper_branch_name}"
102
- elsif line =~ LEADING_PLUS_REGEX
103
- "+ #{proper_branch_name}"
104
- else
105
- proper_branch_name
106
- end
107
- remote_info = tidied_line[REMOTE_INFO_REGEX, :remote_info]
108
- upstream_info =
109
- if remote_info.nil?
110
- "No upstream"
111
- else
112
- comparison_raw = remote_info.split(":")
113
- comparison_raw.length < 2 ? "Up to date" : comparison_raw[1].strip.capitalize
114
- end
115
- [raw_branch_name, upstream_info]
116
- end.to_h
117
- end
118
-
119
109
  def self.delete_multi(*branches)
120
110
  Util.command_output("git branch -D #{branches.map(&:proper_name).join(" ")} --")
121
111
  end
@@ -26,17 +26,11 @@ module GitCurate
26
26
  return EXIT_FAILURE
27
27
  end
28
28
 
29
- if interactive?
30
- puts
31
- print_help
32
- puts
33
- end
34
-
35
29
  branches = Branch.local
36
30
  branches.reject!(&:current?) if interactive?
37
31
 
38
32
  table = Tabulo::Table.new(branches, border: :reduced_ascii, column_padding: 0, align_header: :left) do |t|
39
- t.add_column(:branch, header: "Branch") { |b| b.displayable_name(pad: !interactive?) }
33
+ t.add_column("Branch") { |b| b.displayable_name(pad: !interactive?) }
40
34
  t.add_column("Last commit:#{$/}Date", &:last_commit_date)
41
35
  t.add_column("#{$/}Hash", &:hash)
42
36
  t.add_column("#{$/}Author", &:last_author)
@@ -57,6 +51,15 @@ module GitCurate
57
51
  return EXIT_SUCCESS
58
52
  end
59
53
 
54
+ if branches.empty?
55
+ puts "There are no local branches that can be deleted."
56
+ return EXIT_SUCCESS
57
+ end
58
+
59
+ puts
60
+ print_help
61
+ puts
62
+
60
63
  table.each_with_index do |row, index|
61
64
  case HighLine.ask("#{row} #{prompt}").downcase
62
65
  when "d"
@@ -1,3 +1,3 @@
1
1
  module GitCurate
2
- VERSION = "1.0.1"
2
+ VERSION = "1.2.0.beta"
3
3
  end
data/play.rb ADDED
@@ -0,0 +1,31 @@
1
+ require "rugged"
2
+
3
+ repo = Rugged::Repository.new(".")
4
+
5
+ current_branch_name = repo.head.name.sub(/^refs\/heads\//, '')
6
+
7
+ repo.branches.each_name(:local) do |branch_name|
8
+ branch_reference = repo.references["refs/heads/#{branch_name}"]
9
+ branch = repo.branches[branch_name]
10
+ is_current = current_branch_name == branch_name
11
+ target_id = branch.target_id
12
+ top_commit = branch_reference.log.first
13
+ commit = repo.lookup(target_id)
14
+
15
+ target = branch.resolve.target
16
+ merged = repo.merge_base(repo.head.target, target) == target.oid
17
+
18
+ puts "Branch: #{is_current ? '* ' + branch_name : branch_name}"
19
+ puts "date: #{commit.time}"
20
+ puts "hash: #{target_id}"
21
+ puts "author: #{top_commit[:committer][:name]}"
22
+ puts "subject: #{commit.message.split($/, -1)[0]}"
23
+ if branch.upstream
24
+ puts "upstream: #{branch.upstream.name}"
25
+ ahead, behind = repo.ahead_behind(target_id, branch.upstream.target_id)
26
+ puts "ahead: #{ahead}"
27
+ puts "behind: #{behind}"
28
+ end
29
+ puts "merged: #{merged ? 'Merged' : 'Not merged'}"
30
+ puts "*****************"
31
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: git_curate
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.2.0.beta
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matthew Harvey
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-05-01 00:00:00.000000000 Z
11
+ date: 2021-05-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: highline
@@ -30,28 +30,28 @@ dependencies:
30
30
  requirements:
31
31
  - - '='
32
32
  - !ruby/object:Gem::Version
33
- version: 2.5.0
33
+ version: 2.6.2
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - '='
39
39
  - !ruby/object:Gem::Version
40
- version: 2.5.0
40
+ version: 2.6.2
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: tty-screen
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - '='
46
46
  - !ruby/object:Gem::Version
47
- version: 0.7.1
47
+ version: 0.8.1
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - '='
53
53
  - !ruby/object:Gem::Version
54
- version: 0.7.1
54
+ version: 0.8.1
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: bundler
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -168,13 +168,14 @@ files:
168
168
  - lib/git_curate/runner.rb
169
169
  - lib/git_curate/util.rb
170
170
  - lib/git_curate/version.rb
171
+ - play.rb
171
172
  homepage: https://github.com/matt-harvey/git_curate
172
173
  licenses:
173
174
  - MIT
174
175
  metadata:
175
176
  source_code_uri: https://github.com/matt-harvey/git_curate
176
177
  changelog_uri: https://raw.githubusercontent.com/matt-harvey/git_curate/master/CHANGELOG.md
177
- post_install_message:
178
+ post_install_message:
178
179
  rdoc_options: []
179
180
  require_paths:
180
181
  - lib
@@ -185,12 +186,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
185
186
  version: 2.4.9
186
187
  required_rubygems_version: !ruby/object:Gem::Requirement
187
188
  requirements:
188
- - - ">="
189
+ - - ">"
189
190
  - !ruby/object:Gem::Version
190
- version: '0'
191
+ version: 1.3.1
191
192
  requirements: []
192
193
  rubygems_version: 3.1.2
193
- signing_key:
194
+ signing_key:
194
195
  specification_version: 4
195
196
  summary: Simple git branch curation tool
196
197
  test_files: []