git 1.19.1 → 3.1.1

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.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/.commitlintrc.yml +38 -0
  3. data/.github/pull_request_template.md +8 -0
  4. data/.github/workflows/continuous_integration.yml +19 -21
  5. data/.github/workflows/enforce_conventional_commits.yml +28 -0
  6. data/.github/workflows/experimental_continuous_integration.yml +50 -0
  7. data/.github/workflows/release.yml +52 -0
  8. data/.gitignore +3 -0
  9. data/.husky/commit-msg +1 -0
  10. data/.release-please-manifest.json +3 -0
  11. data/.yardopts +0 -1
  12. data/CHANGELOG.md +211 -0
  13. data/CONTRIBUTING.md +290 -102
  14. data/README.md +180 -62
  15. data/Rakefile +7 -0
  16. data/git.gemspec +11 -11
  17. data/lib/git/author.rb +3 -2
  18. data/lib/git/base.rb +222 -59
  19. data/lib/git/branch.rb +2 -0
  20. data/lib/git/branches.rb +15 -14
  21. data/lib/git/command_line.rb +287 -0
  22. data/lib/git/config.rb +7 -1
  23. data/lib/git/diff.rb +2 -0
  24. data/lib/git/errors.rb +206 -0
  25. data/lib/git/escaped_path.rb +1 -1
  26. data/lib/git/index.rb +2 -1
  27. data/lib/git/lib.rb +604 -234
  28. data/lib/git/log.rb +74 -6
  29. data/lib/git/object.rb +80 -76
  30. data/lib/git/path.rb +9 -8
  31. data/lib/git/remote.rb +2 -0
  32. data/lib/git/repository.rb +2 -0
  33. data/lib/git/stash.rb +7 -6
  34. data/lib/git/stashes.rb +11 -10
  35. data/lib/git/status.rb +135 -25
  36. data/lib/git/version.rb +3 -1
  37. data/lib/git/working_directory.rb +2 -0
  38. data/lib/git/worktree.rb +2 -0
  39. data/lib/git/worktrees.rb +2 -0
  40. data/lib/git.rb +21 -7
  41. data/package.json +10 -0
  42. data/release-please-config.json +36 -0
  43. metadata +52 -38
  44. data/.github/stale.yml +0 -25
  45. data/Dockerfile.changelog-rs +0 -12
  46. data/PULL_REQUEST_TEMPLATE.md +0 -9
  47. data/RELEASING.md +0 -70
  48. data/lib/git/base/factory.rb +0 -99
  49. data/lib/git/failed_error.rb +0 -53
  50. data/lib/git/git_execute_error.rb +0 -7
  51. data/lib/git/signaled_error.rb +0 -50
  52. /data/{ISSUE_TEMPLATE.md → .github/issue_template.md} +0 -0
data/lib/git/base.rb CHANGED
@@ -1,17 +1,18 @@
1
- require 'git/base/factory'
1
+ # frozen_string_literal: true
2
+
2
3
  require 'logger'
3
4
  require 'open3'
4
5
 
5
6
  module Git
6
- # Git::Base is the main public interface for interacting with Git commands.
7
+ # The main public interface for interacting with Git commands
7
8
  #
8
9
  # Instead of creating a Git::Base directly, obtain a Git::Base instance by
9
10
  # calling one of the follow {Git} class methods: {Git.open}, {Git.init},
10
11
  # {Git.clone}, or {Git.bare}.
11
12
  #
13
+ # @api public
14
+ #
12
15
  class Base
13
- include Git::Base::Factory
14
-
15
16
  # (see Git.bare)
16
17
  def self.bare(git_dir, options = {})
17
18
  normalize_paths(options, default_repository: git_dir, bare: true)
@@ -37,6 +38,26 @@ module Git
37
38
  @@config ||= Config.new
38
39
  end
39
40
 
41
+ def self.binary_version(binary_path)
42
+ result = nil
43
+ status = nil
44
+
45
+ begin
46
+ result, status = Open3.capture2e(binary_path, "-c", "core.quotePath=true", "-c", "color.ui=false", "version")
47
+ result = result.chomp
48
+ rescue Errno::ENOENT
49
+ raise RuntimeError, "Failed to get git version: #{binary_path} not found"
50
+ end
51
+
52
+ if status.success?
53
+ version = result[/\d+(\.\d+)+/]
54
+ version_parts = version.split('.').collect { |i| i.to_i }
55
+ version_parts.fill(0, version_parts.length...3)
56
+ else
57
+ raise RuntimeError, "Failed to get git version: #{status}\n#{result}"
58
+ end
59
+ end
60
+
40
61
  # (see Git.init)
41
62
  def self.init(directory = '.', options = {})
42
63
  normalize_paths(options, default_working_directory: directory, default_repository: directory, bare: options[:bare])
@@ -68,9 +89,14 @@ module Git
68
89
  result = working_dir
69
90
  status = nil
70
91
 
71
- git_cmd = "#{Git::Base.config.binary_path} -c core.quotePath=true -c color.ui=false rev-parse --show-toplevel 2>&1"
72
- result, status = Open3.capture2(git_cmd, chdir: File.expand_path(working_dir))
73
- result = result.chomp
92
+ raise ArgumentError, "'#{working_dir}' does not exist" unless Dir.exist?(working_dir)
93
+
94
+ begin
95
+ result, status = Open3.capture2e(Git::Base.config.binary_path, "-c", "core.quotePath=true", "-c", "color.ui=false", "rev-parse", "--show-toplevel", chdir: File.expand_path(working_dir))
96
+ result = result.chomp
97
+ rescue Errno::ENOENT
98
+ raise ArgumentError, "Failed to find the root of the worktree: git binary not found"
99
+ end
74
100
 
75
101
  raise ArgumentError, "'#{working_dir}' is not in a git working tree" unless status.success?
76
102
  result
@@ -122,6 +148,62 @@ module Git
122
148
  @index = options[:index] ? Git::Index.new(options[:index], false) : nil
123
149
  end
124
150
 
151
+ # Update the index from the current worktree to prepare the for the next commit
152
+ #
153
+ # @example
154
+ # lib.add('path/to/file')
155
+ # lib.add(['path/to/file1','path/to/file2'])
156
+ # lib.add(all: true)
157
+ #
158
+ # @param [String, Array<String>] paths a file or files to be added to the repository (relative to the worktree root)
159
+ # @param [Hash] options
160
+ #
161
+ # @option options [Boolean] :all Add, modify, and remove index entries to match the worktree
162
+ # @option options [Boolean] :force Allow adding otherwise ignored files
163
+ #
164
+ def add(paths = '.', **options)
165
+ self.lib.add(paths, options)
166
+ end
167
+
168
+ # adds a new remote to this repository
169
+ # url can be a git url or a Git::Base object if it's a local reference
170
+ #
171
+ # @git.add_remote('scotts_git', 'git://repo.or.cz/rubygit.git')
172
+ # @git.fetch('scotts_git')
173
+ # @git.merge('scotts_git/master')
174
+ #
175
+ # Options:
176
+ # :fetch => true
177
+ # :track => <branch_name>
178
+ def add_remote(name, url, opts = {})
179
+ url = url.repo.path if url.is_a?(Git::Base)
180
+ self.lib.remote_add(name, url, opts)
181
+ Git::Remote.new(self, name)
182
+ end
183
+
184
+ # Create a new git tag
185
+ #
186
+ # @example
187
+ # repo.add_tag('tag_name', object_reference)
188
+ # repo.add_tag('tag_name', object_reference, {:options => 'here'})
189
+ # repo.add_tag('tag_name', {:options => 'here'})
190
+ #
191
+ # @param [String] name The name of the tag to add
192
+ # @param [Hash] options Opstions to pass to `git tag`.
193
+ # See [git-tag](https://git-scm.com/docs/git-tag) for more details.
194
+ # @option options [boolean] :annotate Make an unsigned, annotated tag object
195
+ # @option options [boolean] :a An alias for the `:annotate` option
196
+ # @option options [boolean] :d Delete existing tag with the given names.
197
+ # @option options [boolean] :f Replace an existing tag with the given name (instead of failing)
198
+ # @option options [String] :message Use the given tag message
199
+ # @option options [String] :m An alias for the `:message` option
200
+ # @option options [boolean] :s Make a GPG-signed tag.
201
+ #
202
+ def add_tag(name, *options)
203
+ self.lib.tag(name, *options)
204
+ self.tag(name)
205
+ end
206
+
125
207
  # changes current working directory for a block
126
208
  # to the git working directory
127
209
  #
@@ -254,27 +336,11 @@ module Git
254
336
  self.object('HEAD').grep(string, path_limiter, opts)
255
337
  end
256
338
 
257
- # updates the repository index using the working directory content
258
- #
259
- # @example
260
- # git.add
261
- # git.add('path/to/file')
262
- # git.add(['path/to/file1','path/to/file2'])
263
- # git.add(:all => true)
264
- #
265
- # options:
266
- # :all => true
267
- #
268
- # @param [String,Array] paths files paths to be added (optional, default='.')
269
- # @param [Hash] options
270
- # @option options [boolean] :all
271
- # Update the index not only where the working tree has a file matching
272
- # <pathspec> but also where the index already has an entry.
273
- # See [the --all option to git-add](https://git-scm.com/docs/git-add#Documentation/git-add.txt--A)
274
- # for more details.
339
+ # List the files in the worktree that are ignored by git
340
+ # @return [Array<String>] the list of ignored files relative to teh root of the worktree
275
341
  #
276
- def add(paths = '.', **options)
277
- self.lib.add(paths, options)
342
+ def ignored_files
343
+ self.lib.ignored_files
278
344
  end
279
345
 
280
346
  # removes file(s) from the git repository
@@ -409,14 +475,27 @@ module Git
409
475
  self.lib.conflicts(&block)
410
476
  end
411
477
 
412
- # pulls the given branch from the given remote into the current branch
478
+ # Pulls the given branch from the given remote into the current branch
479
+ #
480
+ # @param remote [String] the remote repository to pull from
481
+ # @param branch [String] the branch to pull from
482
+ # @param opts [Hash] options to pass to the pull command
413
483
  #
414
- # @git.pull # pulls from origin/master
415
- # @git.pull('upstream') # pulls from upstream/master
416
- # @git.pull('upstream', 'develope') # pulls from upstream/develop
484
+ # @option opts [Boolean] :allow_unrelated_histories (false) Merges histories of two projects that started their
485
+ # lives independently
486
+ # @example pulls from origin/master
487
+ # @git.pull
488
+ # @example pulls from upstream/master
489
+ # @git.pull('upstream')
490
+ # @example pulls from upstream/develop
491
+ # @git.pull('upstream', 'develop')
417
492
  #
418
- def pull(remote = nil, branch = nil)
419
- self.lib.pull(remote, branch)
493
+ # @return [Void]
494
+ #
495
+ # @raise [Git::FailedError] if the pull fails
496
+ # @raise [ArgumentError] if a branch is given without a remote
497
+ def pull(remote = nil, branch = nil, opts = {})
498
+ self.lib.pull(remote, branch, opts)
420
499
  end
421
500
 
422
501
  # returns an array of Git:Remote objects
@@ -424,22 +503,6 @@ module Git
424
503
  self.lib.remotes.map { |r| Git::Remote.new(self, r) }
425
504
  end
426
505
 
427
- # adds a new remote to this repository
428
- # url can be a git url or a Git::Base object if it's a local reference
429
- #
430
- # @git.add_remote('scotts_git', 'git://repo.or.cz/rubygit.git')
431
- # @git.fetch('scotts_git')
432
- # @git.merge('scotts_git/master')
433
- #
434
- # Options:
435
- # :fetch => true
436
- # :track => <branch_name>
437
- def add_remote(name, url, opts = {})
438
- url = url.repo.path if url.is_a?(Git::Base)
439
- self.lib.remote_add(name, url, opts)
440
- Git::Remote.new(self, name)
441
- end
442
-
443
506
  # sets the url for a remote
444
507
  # url can be a git url or a Git::Base object if it's a local reference
445
508
  #
@@ -463,7 +526,7 @@ module Git
463
526
  self.lib.tags.map { |r| tag(r) }
464
527
  end
465
528
 
466
- # Creates a new git tag (Git::Tag)
529
+ # Create a new git tag
467
530
  #
468
531
  # @example
469
532
  # repo.add_tag('tag_name', object_reference)
@@ -598,27 +661,127 @@ module Git
598
661
  # runs git rev-parse to convert the objectish to a full sha
599
662
  #
600
663
  # @example
601
- # git.revparse("HEAD^^")
602
- # git.revparse('v2.4^{tree}')
603
- # git.revparse('v2.4:/doc/index.html')
664
+ # git.rev_parse("HEAD^^")
665
+ # git.rev_parse('v2.4^{tree}')
666
+ # git.rev_parse('v2.4:/doc/index.html')
604
667
  #
605
- def revparse(objectish)
606
- self.lib.revparse(objectish)
668
+ def rev_parse(objectish)
669
+ self.lib.rev_parse(objectish)
607
670
  end
608
671
 
609
- def ls_tree(objectish)
610
- self.lib.ls_tree(objectish)
672
+ # For backwards compatibility
673
+ alias revparse rev_parse
674
+
675
+ def ls_tree(objectish, opts = {})
676
+ self.lib.ls_tree(objectish, opts)
611
677
  end
612
678
 
613
679
  def cat_file(objectish)
614
- self.lib.object_contents(objectish)
680
+ self.lib.cat_file(objectish)
615
681
  end
616
682
 
617
- # returns the name of the branch the working directory is currently on
683
+ # The name of the branch HEAD refers to or 'HEAD' if detached
684
+ #
685
+ # Returns one of the following:
686
+ # * The branch name that HEAD refers to (even if it is an unborn branch)
687
+ # * 'HEAD' if in a detached HEAD state
688
+ #
689
+ # @return [String] the name of the branch HEAD refers to or 'HEAD' if detached
690
+ #
618
691
  def current_branch
619
692
  self.lib.branch_current
620
693
  end
621
694
 
695
+ # @return [Git::Branch] an object for branch_name
696
+ def branch(branch_name = self.current_branch)
697
+ Git::Branch.new(self, branch_name)
698
+ end
699
+
700
+ # @return [Git::Branches] a collection of all the branches in the repository.
701
+ # Each branch is represented as a {Git::Branch}.
702
+ def branches
703
+ Git::Branches.new(self)
704
+ end
705
+
706
+ # returns a Git::Worktree object for dir, commitish
707
+ def worktree(dir, commitish = nil)
708
+ Git::Worktree.new(self, dir, commitish)
709
+ end
710
+
711
+ # returns a Git::worktrees object of all the Git::Worktrees
712
+ # objects for this repo
713
+ def worktrees
714
+ Git::Worktrees.new(self)
715
+ end
716
+
717
+ # @return [Git::Object::Commit] a commit object
718
+ def commit_tree(tree = nil, opts = {})
719
+ Git::Object::Commit.new(self, self.lib.commit_tree(tree, opts))
720
+ end
721
+
722
+ # @return [Git::Diff] a Git::Diff object
723
+ def diff(objectish = 'HEAD', obj2 = nil)
724
+ Git::Diff.new(self, objectish, obj2)
725
+ end
726
+
727
+ # @return [Git::Object] a Git object
728
+ def gblob(objectish)
729
+ Git::Object.new(self, objectish, 'blob')
730
+ end
731
+
732
+ # @return [Git::Object] a Git object
733
+ def gcommit(objectish)
734
+ Git::Object.new(self, objectish, 'commit')
735
+ end
736
+
737
+ # @return [Git::Object] a Git object
738
+ def gtree(objectish)
739
+ Git::Object.new(self, objectish, 'tree')
740
+ end
741
+
742
+ # @return [Git::Log] a log with the specified number of commits
743
+ def log(count = 30)
744
+ Git::Log.new(self, count)
745
+ end
746
+
747
+ # returns a Git::Object of the appropriate type
748
+ # you can also call @git.gtree('tree'), but that's
749
+ # just for readability. If you call @git.gtree('HEAD') it will
750
+ # still return a Git::Object::Commit object.
751
+ #
752
+ # object calls a method that will run a rev-parse
753
+ # on the objectish and determine the type of the object and return
754
+ # an appropriate object for that type
755
+ #
756
+ # @return [Git::Object] an instance of the appropriate type of Git::Object
757
+ def object(objectish)
758
+ Git::Object.new(self, objectish)
759
+ end
760
+
761
+ # @return [Git::Remote] a remote of the specified name
762
+ def remote(remote_name = 'origin')
763
+ Git::Remote.new(self, remote_name)
764
+ end
765
+
766
+ # @return [Git::Status] a status object
767
+ def status
768
+ Git::Status.new(self)
769
+ end
770
+
771
+ # @return [Git::Object::Tag] a tag object
772
+ def tag(tag_name)
773
+ Git::Object.new(self, tag_name, 'tag', true)
774
+ end
775
+
776
+ # Find as good common ancestors as possible for a merge
777
+ # example: g.merge_base('master', 'some_branch', 'some_sha', octopus: true)
778
+ #
779
+ # @return [Array<Git::Object::Commit>] a collection of common ancestors
780
+ def merge_base(*args)
781
+ shas = self.lib.merge_base(*args)
782
+ shas.map { |sha| gcommit(sha) }
783
+ end
784
+
622
785
  private
623
786
 
624
787
  # Normalize options before they are sent to Git::Base.new
data/lib/git/branch.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'git/path'
2
4
 
3
5
  module Git
data/lib/git/branches.rb CHANGED
@@ -1,15 +1,17 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Git
2
-
4
+
3
5
  # object that holds all the available branches
4
6
  class Branches
5
7
 
6
8
  include Enumerable
7
-
9
+
8
10
  def initialize(base)
9
11
  @branches = {}
10
-
12
+
11
13
  @base = base
12
-
14
+
13
15
  @base.lib.branches_all.each do |b|
14
16
  @branches[b[0]] = Git::Branch.new(@base, b[0])
15
17
  end
@@ -18,21 +20,21 @@ module Git
18
20
  def local
19
21
  self.select { |b| !b.remote }
20
22
  end
21
-
23
+
22
24
  def remote
23
25
  self.select { |b| b.remote }
24
26
  end
25
-
27
+
26
28
  # array like methods
27
29
 
28
30
  def size
29
31
  @branches.size
30
- end
31
-
32
+ end
33
+
32
34
  def each(&block)
33
35
  @branches.values.each(&block)
34
36
  end
35
-
37
+
36
38
  # Returns the target branch
37
39
  #
38
40
  # Example:
@@ -50,14 +52,14 @@ module Git
50
52
  @branches.values.inject(@branches) do |branches, branch|
51
53
  branches[branch.full] ||= branch
52
54
 
53
- # This is how Git (version 1.7.9.5) works.
54
- # Lets you ignore the 'remotes' if its at the beginning of the branch full name (even if is not a real remote branch).
55
+ # This is how Git (version 1.7.9.5) works.
56
+ # Lets you ignore the 'remotes' if its at the beginning of the branch full name (even if is not a real remote branch).
55
57
  branches[branch.full.sub('remotes/', '')] ||= branch if branch.full =~ /^remotes\/.+/
56
-
58
+
57
59
  branches
58
60
  end[branch_name.to_s]
59
61
  end
60
-
62
+
61
63
  def to_s
62
64
  out = ''
63
65
  @branches.each do |k, b|
@@ -65,7 +67,6 @@ module Git
65
67
  end
66
68
  out
67
69
  end
68
-
69
70
  end
70
71
 
71
72
  end