git 1.19.1 → 5.5.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 +4 -4
- data/.yardopts +5 -1
- data/AI_POLICY.md +24 -0
- data/CHANGELOG.md +689 -0
- data/CODE_OF_CONDUCT.md +25 -0
- data/CONTRIBUTING.md +1175 -97
- data/GOVERNANCE.md +106 -0
- data/LICENSE +1 -1
- data/MAINTAINERS.md +17 -4
- data/README.md +476 -320
- data/UPGRADING.md +1138 -0
- data/git.gemspec +124 -36
- data/lib/git/author.rb +39 -7
- data/lib/git/author_info.rb +66 -0
- data/lib/git/branch.rb +615 -65
- data/lib/git/branch_delete_failure.rb +34 -0
- data/lib/git/branch_delete_result.rb +66 -0
- data/lib/git/branch_info.rb +237 -0
- data/lib/git/branches.rb +167 -44
- data/lib/git/command_line/base.rb +247 -0
- data/lib/git/command_line/capturing.rb +308 -0
- data/lib/git/command_line/result.rb +88 -0
- data/lib/git/command_line/streaming.rb +236 -0
- data/lib/git/command_line.rb +52 -0
- data/lib/git/commands/add.rb +139 -0
- data/lib/git/commands/am/abort.rb +43 -0
- data/lib/git/commands/am/apply.rb +263 -0
- data/lib/git/commands/am/continue.rb +43 -0
- data/lib/git/commands/am/quit.rb +43 -0
- data/lib/git/commands/am/retry.rb +49 -0
- data/lib/git/commands/am/show_current_patch.rb +64 -0
- data/lib/git/commands/am/skip.rb +42 -0
- data/lib/git/commands/am.rb +33 -0
- data/lib/git/commands/apply.rb +242 -0
- data/lib/git/commands/archive/list_formats.rb +46 -0
- data/lib/git/commands/archive.rb +145 -0
- data/lib/git/commands/arguments.rb +4521 -0
- data/lib/git/commands/base.rb +502 -0
- data/lib/git/commands/branch/copy.rb +102 -0
- data/lib/git/commands/branch/create.rb +177 -0
- data/lib/git/commands/branch/delete.rb +88 -0
- data/lib/git/commands/branch/list.rb +178 -0
- data/lib/git/commands/branch/move.rb +102 -0
- data/lib/git/commands/branch/set_upstream.rb +86 -0
- data/lib/git/commands/branch/show_current.rb +49 -0
- data/lib/git/commands/branch/unset_upstream.rb +53 -0
- data/lib/git/commands/branch.rb +34 -0
- data/lib/git/commands/cat_file/batch.rb +385 -0
- data/lib/git/commands/cat_file/filtered.rb +105 -0
- data/lib/git/commands/cat_file/raw.rb +271 -0
- data/lib/git/commands/cat_file.rb +49 -0
- data/lib/git/commands/checkout/branch.rb +153 -0
- data/lib/git/commands/checkout/files.rb +116 -0
- data/lib/git/commands/checkout.rb +38 -0
- data/lib/git/commands/checkout_index.rb +106 -0
- data/lib/git/commands/clean.rb +102 -0
- data/lib/git/commands/clone.rb +241 -0
- data/lib/git/commands/commit.rb +273 -0
- data/lib/git/commands/commit_tree.rb +101 -0
- data/lib/git/commands/config_option_syntax/add.rb +86 -0
- data/lib/git/commands/config_option_syntax/get.rb +121 -0
- data/lib/git/commands/config_option_syntax/get_all.rb +118 -0
- data/lib/git/commands/config_option_syntax/get_color.rb +95 -0
- data/lib/git/commands/config_option_syntax/get_color_bool.rb +96 -0
- data/lib/git/commands/config_option_syntax/get_regexp.rb +119 -0
- data/lib/git/commands/config_option_syntax/get_urlmatch.rb +111 -0
- data/lib/git/commands/config_option_syntax/list.rb +111 -0
- data/lib/git/commands/config_option_syntax/remove_section.rb +79 -0
- data/lib/git/commands/config_option_syntax/rename_section.rb +83 -0
- data/lib/git/commands/config_option_syntax/replace_all.rb +109 -0
- data/lib/git/commands/config_option_syntax/set.rb +119 -0
- data/lib/git/commands/config_option_syntax/unset.rb +92 -0
- data/lib/git/commands/config_option_syntax/unset_all.rb +94 -0
- data/lib/git/commands/config_option_syntax.rb +56 -0
- data/lib/git/commands/describe.rb +156 -0
- data/lib/git/commands/diff.rb +657 -0
- data/lib/git/commands/diff_files.rb +519 -0
- data/lib/git/commands/diff_index.rb +499 -0
- data/lib/git/commands/fetch.rb +354 -0
- data/lib/git/commands/fsck.rb +138 -0
- data/lib/git/commands/gc.rb +134 -0
- data/lib/git/commands/grep.rb +339 -0
- data/lib/git/commands/init.rb +101 -0
- data/lib/git/commands/log.rb +634 -0
- data/lib/git/commands/ls_files.rb +195 -0
- data/lib/git/commands/ls_remote.rb +161 -0
- data/lib/git/commands/ls_tree.rb +135 -0
- data/lib/git/commands/maintenance/register.rb +77 -0
- data/lib/git/commands/maintenance/run.rb +109 -0
- data/lib/git/commands/maintenance/start.rb +71 -0
- data/lib/git/commands/maintenance/stop.rb +60 -0
- data/lib/git/commands/maintenance/unregister.rb +84 -0
- data/lib/git/commands/maintenance.rb +31 -0
- data/lib/git/commands/merge/abort.rb +44 -0
- data/lib/git/commands/merge/continue.rb +44 -0
- data/lib/git/commands/merge/quit.rb +46 -0
- data/lib/git/commands/merge/start.rb +250 -0
- data/lib/git/commands/merge.rb +28 -0
- data/lib/git/commands/merge_base.rb +91 -0
- data/lib/git/commands/mv.rb +82 -0
- data/lib/git/commands/name_rev.rb +119 -0
- data/lib/git/commands/pull.rb +382 -0
- data/lib/git/commands/push.rb +251 -0
- data/lib/git/commands/read_tree.rb +154 -0
- data/lib/git/commands/remote/add.rb +96 -0
- data/lib/git/commands/remote/get_url.rb +68 -0
- data/lib/git/commands/remote/list.rb +56 -0
- data/lib/git/commands/remote/prune.rb +63 -0
- data/lib/git/commands/remote/remove.rb +52 -0
- data/lib/git/commands/remote/rename.rb +76 -0
- data/lib/git/commands/remote/set_branches.rb +70 -0
- data/lib/git/commands/remote/set_head.rb +89 -0
- data/lib/git/commands/remote/set_url.rb +78 -0
- data/lib/git/commands/remote/set_url_add.rb +70 -0
- data/lib/git/commands/remote/set_url_delete.rb +71 -0
- data/lib/git/commands/remote/show.rb +77 -0
- data/lib/git/commands/remote/update.rb +79 -0
- data/lib/git/commands/remote.rb +42 -0
- data/lib/git/commands/repack.rb +281 -0
- data/lib/git/commands/reset.rb +154 -0
- data/lib/git/commands/rev_parse.rb +304 -0
- data/lib/git/commands/revert/abort.rb +45 -0
- data/lib/git/commands/revert/continue.rb +62 -0
- data/lib/git/commands/revert/quit.rb +47 -0
- data/lib/git/commands/revert/skip.rb +44 -0
- data/lib/git/commands/revert/start.rb +158 -0
- data/lib/git/commands/revert.rb +29 -0
- data/lib/git/commands/rm.rb +113 -0
- data/lib/git/commands/show.rb +632 -0
- data/lib/git/commands/show_ref/exclude_existing.rb +119 -0
- data/lib/git/commands/show_ref/exists.rb +80 -0
- data/lib/git/commands/show_ref/list.rb +149 -0
- data/lib/git/commands/show_ref/verify.rb +122 -0
- data/lib/git/commands/show_ref.rb +42 -0
- data/lib/git/commands/stash/apply.rb +81 -0
- data/lib/git/commands/stash/branch.rb +67 -0
- data/lib/git/commands/stash/clear.rb +43 -0
- data/lib/git/commands/stash/create.rb +60 -0
- data/lib/git/commands/stash/drop.rb +73 -0
- data/lib/git/commands/stash/list.rb +43 -0
- data/lib/git/commands/stash/pop.rb +87 -0
- data/lib/git/commands/stash/push.rb +112 -0
- data/lib/git/commands/stash/show.rb +158 -0
- data/lib/git/commands/stash/store.rb +72 -0
- data/lib/git/commands/stash.rb +38 -0
- data/lib/git/commands/status.rb +174 -0
- data/lib/git/commands/symbolic_ref/delete.rb +72 -0
- data/lib/git/commands/symbolic_ref/read.rb +99 -0
- data/lib/git/commands/symbolic_ref/update.rb +79 -0
- data/lib/git/commands/symbolic_ref.rb +38 -0
- data/lib/git/commands/tag/create.rb +142 -0
- data/lib/git/commands/tag/delete.rb +57 -0
- data/lib/git/commands/tag/list.rb +146 -0
- data/lib/git/commands/tag/verify.rb +71 -0
- data/lib/git/commands/tag.rb +26 -0
- data/lib/git/commands/update_ref/batch.rb +145 -0
- data/lib/git/commands/update_ref/delete.rb +90 -0
- data/lib/git/commands/update_ref/update.rb +103 -0
- data/lib/git/commands/update_ref.rb +42 -0
- data/lib/git/commands/version.rb +60 -0
- data/lib/git/commands/worktree/add.rb +139 -0
- data/lib/git/commands/worktree/list.rb +64 -0
- data/lib/git/commands/worktree/lock.rb +58 -0
- data/lib/git/commands/worktree/management_base.rb +51 -0
- data/lib/git/commands/worktree/move.rb +66 -0
- data/lib/git/commands/worktree/prune.rb +67 -0
- data/lib/git/commands/worktree/remove.rb +63 -0
- data/lib/git/commands/worktree/repair.rb +76 -0
- data/lib/git/commands/worktree/unlock.rb +47 -0
- data/lib/git/commands/worktree.rb +43 -0
- data/lib/git/commands/write_tree.rb +68 -0
- data/lib/git/commands.rb +88 -0
- data/lib/git/config.rb +72 -5
- data/lib/git/config_entry_info.rb +106 -0
- data/lib/git/configuring.rb +795 -0
- data/lib/git/detached_head_info.rb +57 -0
- data/lib/git/diff.rb +437 -86
- data/lib/git/diff_file_numstat_info.rb +31 -0
- data/lib/git/diff_file_patch_info.rb +136 -0
- data/lib/git/diff_file_raw_info.rb +129 -0
- data/lib/git/diff_info.rb +162 -0
- data/lib/git/diff_path_status.rb +107 -0
- data/lib/git/diff_result.rb +34 -0
- data/lib/git/diff_stats.rb +111 -0
- data/lib/git/dirstat_info.rb +102 -0
- data/lib/git/encoding_utils.rb +32 -1
- data/lib/git/errors.rb +285 -0
- data/lib/git/escaped_path.rb +57 -5
- data/lib/git/execution_context/global.rb +31 -0
- data/lib/git/execution_context/repository.rb +151 -0
- data/lib/git/execution_context.rb +559 -0
- data/lib/git/factories.rb +813 -0
- data/lib/git/file_ref.rb +77 -0
- data/lib/git/fsck_object.rb +56 -0
- data/lib/git/fsck_result.rb +132 -0
- data/lib/git/log.rb +306 -90
- data/lib/git/object.rb +563 -141
- data/lib/git/parsers/branch.rb +240 -0
- data/lib/git/parsers/cat_file.rb +111 -0
- data/lib/git/parsers/config_entry.rb +110 -0
- data/lib/git/parsers/diff.rb +792 -0
- data/lib/git/parsers/fsck.rb +144 -0
- data/lib/git/parsers/grep.rb +42 -0
- data/lib/git/parsers/ls_remote.rb +79 -0
- data/lib/git/parsers/ls_tree.rb +58 -0
- data/lib/git/parsers/remote.rb +162 -0
- data/lib/git/parsers/stash.rb +292 -0
- data/lib/git/parsers/status.rb +251 -0
- data/lib/git/parsers/tag.rb +341 -0
- data/lib/git/parsers/worktree.rb +185 -0
- data/lib/git/path_resolver.rb +206 -0
- data/lib/git/remote.rb +165 -12
- data/lib/git/remote_info.rb +203 -0
- data/lib/git/repository/branching.rb +964 -0
- data/lib/git/repository/committing.rb +246 -0
- data/lib/git/repository/context_helpers.rb +293 -0
- data/lib/git/repository/diffing.rb +785 -0
- data/lib/git/repository/inspecting.rb +252 -0
- data/lib/git/repository/logging.rb +410 -0
- data/lib/git/repository/maintenance.rb +65 -0
- data/lib/git/repository/merging.rb +451 -0
- data/lib/git/repository/object_operations.rb +1551 -0
- data/lib/git/repository/remote_operations.rb +984 -0
- data/lib/git/repository/shared_private.rb +120 -0
- data/lib/git/repository/staging.rb +587 -0
- data/lib/git/repository/stashing.rb +623 -0
- data/lib/git/repository/status_operations.rb +249 -0
- data/lib/git/repository/worktree_operations.rb +339 -0
- data/lib/git/repository.rb +484 -2
- data/lib/git/stash.rb +109 -12
- data/lib/git/stash_info.rb +102 -0
- data/lib/git/stashes.rb +169 -26
- data/lib/git/status.rb +308 -122
- data/lib/git/status_file_info.rb +258 -0
- data/lib/git/status_info.rb +189 -0
- data/lib/git/tag_delete_failure.rb +34 -0
- data/lib/git/tag_delete_result.rb +66 -0
- data/lib/git/tag_info.rb +99 -0
- data/lib/git/url.rb +15 -8
- data/lib/git/version.rb +113 -2
- data/lib/git/version_constraint.rb +85 -0
- data/lib/git/worktree.rb +150 -8
- data/lib/git/worktree_info.rb +128 -0
- data/lib/git/worktrees.rb +118 -13
- data/lib/git.rb +632 -234
- metadata +369 -54
- data/.github/stale.yml +0 -25
- data/.github/workflows/continuous_integration.yml +0 -49
- data/.gitignore +0 -10
- data/Dockerfile.changelog-rs +0 -12
- data/Gemfile +0 -5
- data/ISSUE_TEMPLATE.md +0 -15
- data/PULL_REQUEST_TEMPLATE.md +0 -9
- data/RELEASING.md +0 -70
- data/Rakefile +0 -60
- data/lib/git/base/factory.rb +0 -99
- data/lib/git/base.rb +0 -711
- data/lib/git/command_line_result.rb +0 -86
- data/lib/git/failed_error.rb +0 -53
- data/lib/git/git_execute_error.rb +0 -7
- data/lib/git/index.rb +0 -5
- data/lib/git/lib.rb +0 -1328
- data/lib/git/path.rb +0 -31
- data/lib/git/signaled_error.rb +0 -50
- data/lib/git/working_directory.rb +0 -4
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Git
|
|
4
|
+
# Immutable value object representing a single directory's contribution to a diff
|
|
5
|
+
#
|
|
6
|
+
# @example Create a DirstatEntry
|
|
7
|
+
# info = Git::DirstatEntry.new(directory: 'lib/commands/', percentage: 45.2)
|
|
8
|
+
# info.directory #=> "lib/commands/"
|
|
9
|
+
# info.percentage #=> 45.2
|
|
10
|
+
#
|
|
11
|
+
# @api private
|
|
12
|
+
#
|
|
13
|
+
# Work in progress; this class is internal for now and may be made public in a future release.
|
|
14
|
+
#
|
|
15
|
+
# @!attribute [r] directory
|
|
16
|
+
# @return [String] the directory path (always ends with '/')
|
|
17
|
+
#
|
|
18
|
+
# @!attribute [r] percentage
|
|
19
|
+
# @return [Float] the percentage of changes in this directory (0.0-100.0)
|
|
20
|
+
#
|
|
21
|
+
DirstatEntry = Data.define(:directory, :percentage)
|
|
22
|
+
|
|
23
|
+
# Immutable result object from git --dirstat output
|
|
24
|
+
#
|
|
25
|
+
# Contains the list of directories and their contribution percentages to the diff.
|
|
26
|
+
#
|
|
27
|
+
# @example Create a DirstatInfo
|
|
28
|
+
# dirstat = Git::DirstatInfo.new(
|
|
29
|
+
# entries: [
|
|
30
|
+
# Git::DirstatEntry.new(directory: 'lib/commands/', percentage: 45.2),
|
|
31
|
+
# Git::DirstatEntry.new(directory: 'spec/unit/', percentage: 30.1)
|
|
32
|
+
# ]
|
|
33
|
+
# )
|
|
34
|
+
# dirstat.entries.first.directory #=> "lib/commands/"
|
|
35
|
+
# dirstat['lib/commands/'] #=> 45.2
|
|
36
|
+
# dirstat.to_h #=> { "lib/commands/" => 45.2, "spec/unit/" => 30.1 }
|
|
37
|
+
#
|
|
38
|
+
# @api private
|
|
39
|
+
#
|
|
40
|
+
# Work in progress; this class is internal for now and may be made public in a future release.
|
|
41
|
+
#
|
|
42
|
+
# @!attribute [r] entries
|
|
43
|
+
# @return [Array<DirstatEntry>] directory statistics in order from git output
|
|
44
|
+
#
|
|
45
|
+
DirstatInfo = Data.define(:entries) do
|
|
46
|
+
# Look up percentage by directory path
|
|
47
|
+
#
|
|
48
|
+
# @param directory [String] the directory path
|
|
49
|
+
#
|
|
50
|
+
# @return [Float, nil] the percentage or nil if not found
|
|
51
|
+
#
|
|
52
|
+
def [](directory)
|
|
53
|
+
entries.find { |e| e.directory == directory }&.percentage
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Convert to a Hash mapping directory to percentage
|
|
57
|
+
#
|
|
58
|
+
# @return [Hash<String, Float>]
|
|
59
|
+
#
|
|
60
|
+
def to_h
|
|
61
|
+
entries.to_h { |e| [e.directory, e.percentage] }
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Number of directories in the dirstat
|
|
65
|
+
#
|
|
66
|
+
# @return [Integer]
|
|
67
|
+
#
|
|
68
|
+
def size
|
|
69
|
+
entries.size
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Check if dirstat is empty
|
|
73
|
+
#
|
|
74
|
+
# @return [Boolean]
|
|
75
|
+
#
|
|
76
|
+
def empty?
|
|
77
|
+
entries.empty?
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Iterate over entries
|
|
81
|
+
#
|
|
82
|
+
# @overload each
|
|
83
|
+
#
|
|
84
|
+
# @return [Enumerator<Git::DirstatEntry>] an enumerator over all dirstat entries
|
|
85
|
+
#
|
|
86
|
+
# @overload each(&block)
|
|
87
|
+
#
|
|
88
|
+
# @return [Array<Git::DirstatEntry>] the full list of dirstat entries
|
|
89
|
+
#
|
|
90
|
+
# @yield [entry] each dirstat entry
|
|
91
|
+
#
|
|
92
|
+
# @yieldparam entry [Git::DirstatEntry] a single dirstat entry
|
|
93
|
+
#
|
|
94
|
+
# @yieldreturn [void]
|
|
95
|
+
#
|
|
96
|
+
def each(&block)
|
|
97
|
+
entries.each(&block)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
include Enumerable
|
|
101
|
+
end
|
|
102
|
+
end
|
data/lib/git/encoding_utils.rb
CHANGED
|
@@ -3,25 +3,56 @@
|
|
|
3
3
|
require 'rchardet'
|
|
4
4
|
|
|
5
5
|
module Git
|
|
6
|
-
#
|
|
6
|
+
# Provides helpers for detecting and normalizing string encodings
|
|
7
|
+
#
|
|
8
|
+
# `default_encoding` refers to {Git::EncodingUtils.default_encoding}, which is
|
|
9
|
+
# derived from this source file's encoding declaration
|
|
10
|
+
#
|
|
11
|
+
# @api private
|
|
12
|
+
#
|
|
7
13
|
module EncodingUtils
|
|
14
|
+
# Returns the default encoding name used by this source file
|
|
15
|
+
#
|
|
16
|
+
# @return [String] the source file encoding name
|
|
17
|
+
#
|
|
8
18
|
def self.default_encoding
|
|
9
19
|
__ENCODING__.name
|
|
10
20
|
end
|
|
11
21
|
|
|
22
|
+
# Returns the fallback encoding name used when detection fails
|
|
23
|
+
#
|
|
24
|
+
# @return [String] the fallback encoding name
|
|
25
|
+
#
|
|
12
26
|
def self.best_guess_encoding
|
|
13
27
|
# Encoding::ASCII_8BIT.name
|
|
14
28
|
Encoding::UTF_8.name
|
|
15
29
|
end
|
|
16
30
|
|
|
31
|
+
# Returns the detected encoding name for the given string
|
|
32
|
+
#
|
|
33
|
+
# @param str [String] the string whose encoding should be detected
|
|
34
|
+
#
|
|
35
|
+
# @return [String] the detected encoding name or the fallback encoding name
|
|
36
|
+
#
|
|
17
37
|
def self.detected_encoding(str)
|
|
18
38
|
CharDet.detect(str)['encoding'] || best_guess_encoding
|
|
19
39
|
end
|
|
20
40
|
|
|
41
|
+
# Returns replacement options used when transcoding invalid byte sequences
|
|
42
|
+
#
|
|
43
|
+
# @return [Hash<Symbol, Symbol>] options for replacing invalid and undefined bytes
|
|
44
|
+
#
|
|
21
45
|
def self.encoding_options
|
|
22
46
|
{ invalid: :replace, undef: :replace }
|
|
23
47
|
end
|
|
24
48
|
|
|
49
|
+
# Returns the given string converted to {Git::EncodingUtils.default_encoding}
|
|
50
|
+
#
|
|
51
|
+
# @param str [String] the string to normalize
|
|
52
|
+
#
|
|
53
|
+
# @return [String] the original or transcoded string in
|
|
54
|
+
# {Git::EncodingUtils.default_encoding}
|
|
55
|
+
#
|
|
25
56
|
def self.normalize_encoding(str)
|
|
26
57
|
return str if str.valid_encoding? && str.encoding.name == default_encoding
|
|
27
58
|
|
data/lib/git/errors.rb
ADDED
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Git
|
|
4
|
+
# rubocop:disable Layout/LineLength
|
|
5
|
+
|
|
6
|
+
# Base class for all custom git module errors
|
|
7
|
+
#
|
|
8
|
+
# The git gem will only raise an `ArgumentError` or an error that is a subclass of
|
|
9
|
+
# `Git::Error`. It does not explicitly raise any other types of errors.
|
|
10
|
+
#
|
|
11
|
+
# It is recommended to rescue `Git::Error` to catch any runtime error raised by
|
|
12
|
+
# this gem unless you need more specific error handling.
|
|
13
|
+
#
|
|
14
|
+
# Git's custom errors are arranged in the following class heirarchy:
|
|
15
|
+
#
|
|
16
|
+
# ```text
|
|
17
|
+
# StandardError
|
|
18
|
+
# └─> Git::Error
|
|
19
|
+
# ├─> Git::CommandLineError
|
|
20
|
+
# │ ├─> Git::FailedError
|
|
21
|
+
# │ └─> Git::SignaledError
|
|
22
|
+
# │ └─> Git::TimeoutError
|
|
23
|
+
# ├─> Git::ProcessIOError
|
|
24
|
+
# ├─> Git::UnexpectedResultError
|
|
25
|
+
# └─> Git::VersionError
|
|
26
|
+
# ```
|
|
27
|
+
#
|
|
28
|
+
# | Error Class | Description |
|
|
29
|
+
# | --- | --- |
|
|
30
|
+
# | `Error` | This catch-all error serves as the base class for other custom errors raised by the git gem. |
|
|
31
|
+
# | `CommandLineError` | A subclass of this error is raised when there is a problem executing the git command line. |
|
|
32
|
+
# | `FailedError` | This error is raised when the git command line exits with a non-zero status code that is not expected by the git gem. |
|
|
33
|
+
# | `SignaledError` | This error is raised when the git command line is terminated as a result of receiving a signal. This could happen if the process is forcibly terminated or if there is a serious system error. |
|
|
34
|
+
# | `TimeoutError` | This is a specific type of `SignaledError` that is raised when the git command line operation times out and is killed via the SIGKILL signal. This happens if the operation takes longer than the timeout duration configured in `Git.config.timeout` or via the `:timeout` parameter given in git methods that support timeouts. |
|
|
35
|
+
# | `ProcessIOError` | An error was encountered reading or writing to a subprocess. |
|
|
36
|
+
# | `UnexpectedResultError` | The command line ran without error but did not return the expected results. |
|
|
37
|
+
# | `VersionError` | The installed git version does not meet the requirements of the git gem or a specific command. |
|
|
38
|
+
#
|
|
39
|
+
# @example Rescuing a generic error
|
|
40
|
+
# begin
|
|
41
|
+
# # some git operation
|
|
42
|
+
# rescue Git::Error => e
|
|
43
|
+
# puts "An error occurred: #{e.message}"
|
|
44
|
+
# end
|
|
45
|
+
#
|
|
46
|
+
# @example Rescuing a timeout error
|
|
47
|
+
# begin
|
|
48
|
+
# timeout_duration = 0.001 # seconds
|
|
49
|
+
# repo = Git.clone('https://github.com/ruby-git/ruby-git', 'ruby-git-temp', timeout: timeout_duration)
|
|
50
|
+
# rescue Git::TimeoutError => e # Catch the more specific error first!
|
|
51
|
+
# puts "Git clone took too long and timed out #{e}"
|
|
52
|
+
# rescue Git::Error => e
|
|
53
|
+
# puts "Received the following error: #{e}"
|
|
54
|
+
# end
|
|
55
|
+
#
|
|
56
|
+
# @see Git::CommandLineError
|
|
57
|
+
#
|
|
58
|
+
# @see Git::FailedError
|
|
59
|
+
#
|
|
60
|
+
# @see Git::SignaledError
|
|
61
|
+
#
|
|
62
|
+
# @see Git::TimeoutError
|
|
63
|
+
#
|
|
64
|
+
# @see Git::ProcessIOError
|
|
65
|
+
#
|
|
66
|
+
# @see Git::UnexpectedResultError
|
|
67
|
+
#
|
|
68
|
+
# @see Git::VersionError
|
|
69
|
+
#
|
|
70
|
+
# @api public
|
|
71
|
+
#
|
|
72
|
+
class Error < StandardError; end
|
|
73
|
+
|
|
74
|
+
# rubocop:enable Layout/LineLength
|
|
75
|
+
|
|
76
|
+
# An alias for Git::Error
|
|
77
|
+
#
|
|
78
|
+
# Git::GitExecuteError error class is an alias for Git::Error for backwards
|
|
79
|
+
# compatibility. It is recommended to use Git::Error directly.
|
|
80
|
+
#
|
|
81
|
+
# @deprecated Use Git::Error instead
|
|
82
|
+
#
|
|
83
|
+
GitExecuteError = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('Git::GitExecuteError', 'Git::Error', Git::Deprecation)
|
|
84
|
+
|
|
85
|
+
# Raised when a git command fails or exits because of an uncaught signal
|
|
86
|
+
#
|
|
87
|
+
# The git command executed, status, stdout, and stderr are available from this
|
|
88
|
+
# object.
|
|
89
|
+
#
|
|
90
|
+
# The Gem will raise a more specific error for each type of failure:
|
|
91
|
+
#
|
|
92
|
+
# * {Git::FailedError}: when the git command exits with a non-zero status
|
|
93
|
+
# * {Git::SignaledError}: when the git command exits because of an uncaught signal
|
|
94
|
+
# * {Git::TimeoutError}: when the git command times out
|
|
95
|
+
#
|
|
96
|
+
# @api public
|
|
97
|
+
#
|
|
98
|
+
class CommandLineError < Git::Error
|
|
99
|
+
# Create a CommandLineError object
|
|
100
|
+
#
|
|
101
|
+
# @example
|
|
102
|
+
# `exit 1` # set $? appropriately for this example
|
|
103
|
+
# result = Git::CommandLine::Result.new(%w[git status], $?, 'stdout', 'stderr')
|
|
104
|
+
# error = Git::CommandLineError.new(result)
|
|
105
|
+
# error.to_s #=> '["git", "status"], status: pid 89784 exit 1, stderr: "stderr"'
|
|
106
|
+
#
|
|
107
|
+
# @param result [Git::CommandLine::Result] the result of the git command including
|
|
108
|
+
# the git command, status, stdout, and stderr
|
|
109
|
+
#
|
|
110
|
+
def initialize(result)
|
|
111
|
+
@result = result
|
|
112
|
+
super(error_message)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# The human readable representation of this error
|
|
116
|
+
#
|
|
117
|
+
# @example
|
|
118
|
+
# error.error_message #=> '["git", "status"], status: pid 89784 exit 1, stderr: "stderr"'
|
|
119
|
+
#
|
|
120
|
+
# @return [String]
|
|
121
|
+
#
|
|
122
|
+
def error_message = <<~MESSAGE.chomp
|
|
123
|
+
#{result.git_cmd}, status: #{result.status}, stderr: #{result.stderr.inspect}
|
|
124
|
+
MESSAGE
|
|
125
|
+
|
|
126
|
+
# The result of the git command including the git command and its status and output
|
|
127
|
+
#
|
|
128
|
+
# @example
|
|
129
|
+
# error.result #=> #<Git::CommandLine::Result:0x00000001046bd488 ...>
|
|
130
|
+
#
|
|
131
|
+
# @return [Git::CommandLine::Result]
|
|
132
|
+
#
|
|
133
|
+
attr_reader :result
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# This error is raised when a git command returns a non-zero exitstatus
|
|
137
|
+
#
|
|
138
|
+
# The git command executed, status, stdout, and stderr are available from this
|
|
139
|
+
# object.
|
|
140
|
+
#
|
|
141
|
+
# @api public
|
|
142
|
+
#
|
|
143
|
+
class FailedError < Git::CommandLineError; end
|
|
144
|
+
|
|
145
|
+
# This error is raised when a git command exits because of an uncaught signal
|
|
146
|
+
#
|
|
147
|
+
# @api public
|
|
148
|
+
#
|
|
149
|
+
class SignaledError < Git::CommandLineError; end
|
|
150
|
+
|
|
151
|
+
# This error is raised when a git command takes longer than the configured timeout
|
|
152
|
+
#
|
|
153
|
+
# The git command executed, status, stdout, and stderr, and the timeout duration
|
|
154
|
+
# are available from this object.
|
|
155
|
+
#
|
|
156
|
+
# result.status.timed_out? will be `true`
|
|
157
|
+
#
|
|
158
|
+
# @api public
|
|
159
|
+
#
|
|
160
|
+
class TimeoutError < Git::SignaledError
|
|
161
|
+
# Create a TimeoutError object
|
|
162
|
+
#
|
|
163
|
+
# @example
|
|
164
|
+
# command = %w[sleep 10]
|
|
165
|
+
# timeout_duration = 1
|
|
166
|
+
# status = ProcessExecuter.spawn(*command, timeout: timeout_duration)
|
|
167
|
+
# result = Git::CommandLine::Result.new(command, status, 'stdout', 'err output')
|
|
168
|
+
# error = Git::TimeoutError.new(result, timeout_duration)
|
|
169
|
+
# error.error_message
|
|
170
|
+
# #=> '["sleep", "10"], status: pid 70144 SIGKILL (signal 9), stderr: "err output", timed out after 1s'
|
|
171
|
+
#
|
|
172
|
+
# @param result [Git::CommandLine::Result] the result of the git command including
|
|
173
|
+
# the git command, status, stdout, and stderr
|
|
174
|
+
#
|
|
175
|
+
# @param timeout_duration [Numeric] the amount of time the subprocess was allowed
|
|
176
|
+
# to run before being killed
|
|
177
|
+
#
|
|
178
|
+
def initialize(result, timeout_duration)
|
|
179
|
+
@timeout_duration = timeout_duration
|
|
180
|
+
super(result)
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
# The human readable representation of this error
|
|
184
|
+
#
|
|
185
|
+
# @example
|
|
186
|
+
# error.error_message
|
|
187
|
+
# #=> '["sleep", "10"], status: pid 88811 SIGKILL (signal 9), stderr: "err output", timed out after 1s'
|
|
188
|
+
#
|
|
189
|
+
# @return [String]
|
|
190
|
+
#
|
|
191
|
+
def error_message = <<~MESSAGE.chomp
|
|
192
|
+
#{super}, timed out after #{timeout_duration}s
|
|
193
|
+
MESSAGE
|
|
194
|
+
|
|
195
|
+
# The amount of time the subprocess was allowed to run before being killed
|
|
196
|
+
#
|
|
197
|
+
# @example
|
|
198
|
+
# `kill -9 $$` # set $? appropriately for this example
|
|
199
|
+
# result = Git::CommandLine::Result.new(%w[git status], $?, '', "killed")
|
|
200
|
+
# error = Git::TimeoutError.new(result, 10)
|
|
201
|
+
# error.timeout_duration #=> 10
|
|
202
|
+
#
|
|
203
|
+
# @return [Numeric]
|
|
204
|
+
#
|
|
205
|
+
attr_reader :timeout_duration
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
# Raised when the output of a git command can not be read
|
|
209
|
+
#
|
|
210
|
+
# @api public
|
|
211
|
+
#
|
|
212
|
+
class ProcessIOError < Git::Error; end
|
|
213
|
+
|
|
214
|
+
# Raised when the git command result was not as expected
|
|
215
|
+
#
|
|
216
|
+
# @api public
|
|
217
|
+
#
|
|
218
|
+
class UnexpectedResultError < Git::Error; end
|
|
219
|
+
|
|
220
|
+
# Raised when the installed git version does not meet requirements
|
|
221
|
+
#
|
|
222
|
+
# This error is raised when:
|
|
223
|
+
# - The installed git version is below `Git::MINIMUM_GIT_VERSION`
|
|
224
|
+
# - A command requires a minimum git version that isn't met
|
|
225
|
+
# - A command was removed in a git version older than the installed version
|
|
226
|
+
#
|
|
227
|
+
# @example Rescuing a version error
|
|
228
|
+
# begin
|
|
229
|
+
# git.some_command
|
|
230
|
+
# rescue Git::VersionError => e
|
|
231
|
+
# puts "Git version #{e.actual_version} does not meet requirements"
|
|
232
|
+
# puts " #{e.subject}: requires #{e.constraint}"
|
|
233
|
+
# end
|
|
234
|
+
#
|
|
235
|
+
# @api public
|
|
236
|
+
#
|
|
237
|
+
class VersionError < Git::Error
|
|
238
|
+
# Create a VersionError
|
|
239
|
+
#
|
|
240
|
+
# @param subject [#to_s] the entity with the version requirement (e.g., "The git gem", a Class)
|
|
241
|
+
#
|
|
242
|
+
# @param constraint [Git::VersionConstraint] the version constraint that was violated
|
|
243
|
+
#
|
|
244
|
+
# @param actual_version [Git::Version] the installed git version
|
|
245
|
+
#
|
|
246
|
+
def initialize(subject:, constraint:, actual_version:)
|
|
247
|
+
@subject = subject
|
|
248
|
+
@constraint = constraint
|
|
249
|
+
@actual_version = actual_version
|
|
250
|
+
super(build_message)
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
# The entity that has the version requirement
|
|
254
|
+
#
|
|
255
|
+
# @return [#to_s]
|
|
256
|
+
#
|
|
257
|
+
attr_reader :subject
|
|
258
|
+
|
|
259
|
+
# The version constraint that was violated
|
|
260
|
+
#
|
|
261
|
+
# @return [Git::VersionConstraint]
|
|
262
|
+
#
|
|
263
|
+
attr_reader :constraint
|
|
264
|
+
|
|
265
|
+
# The installed git version that caused the error
|
|
266
|
+
#
|
|
267
|
+
# @return [Git::Version]
|
|
268
|
+
#
|
|
269
|
+
attr_reader :actual_version
|
|
270
|
+
|
|
271
|
+
private
|
|
272
|
+
|
|
273
|
+
# Builds the error message for the violated version constraint.
|
|
274
|
+
#
|
|
275
|
+
# @return [String]
|
|
276
|
+
#
|
|
277
|
+
def build_message
|
|
278
|
+
if constraint.too_new?(actual_version)
|
|
279
|
+
"#{subject} requires git < #{constraint.before} (found #{actual_version})"
|
|
280
|
+
else
|
|
281
|
+
"#{subject} requires git >= #{constraint.min} (found #{actual_version})"
|
|
282
|
+
end
|
|
283
|
+
end
|
|
284
|
+
end
|
|
285
|
+
end
|
data/lib/git/escaped_path.rb
CHANGED
|
@@ -3,15 +3,20 @@
|
|
|
3
3
|
module Git
|
|
4
4
|
# Represents an escaped Git path string
|
|
5
5
|
#
|
|
6
|
-
# Git commands that output paths (e.g. ls-files, diff), will escape
|
|
6
|
+
# Git commands that output paths (e.g. ls-files, diff), will escape unusual
|
|
7
7
|
# characters in the path with backslashes in the same way C escapes control
|
|
8
8
|
# characters (e.g. \t for TAB, \n for LF, \\ for backslash) or bytes with values
|
|
9
9
|
# larger than 0x80 (e.g. octal \302\265 for "micro" in UTF-8).
|
|
10
10
|
#
|
|
11
|
-
# @example
|
|
12
|
-
# Git::
|
|
11
|
+
# @example Decode octal UTF-8 bytes
|
|
12
|
+
# Git::EscapedPath.new('\302\265').unescape # => "µ"
|
|
13
|
+
#
|
|
14
|
+
# @api private
|
|
13
15
|
#
|
|
14
16
|
class EscapedPath
|
|
17
|
+
# Maps single-character escapes to their decoded byte values
|
|
18
|
+
#
|
|
19
|
+
# @return [Hash<String, Integer>] escape characters mapped to byte values
|
|
15
20
|
UNESCAPES = {
|
|
16
21
|
'a' => 0x07,
|
|
17
22
|
'b' => 0x08,
|
|
@@ -26,13 +31,27 @@ module Git
|
|
|
26
31
|
"'" => 0x27
|
|
27
32
|
}.freeze
|
|
28
33
|
|
|
34
|
+
# Returns the escaped path as provided by git output
|
|
35
|
+
#
|
|
36
|
+
# @return [String] the escaped path string
|
|
29
37
|
attr_reader :path
|
|
30
38
|
|
|
39
|
+
# Initializes an escaped path wrapper
|
|
40
|
+
#
|
|
41
|
+
# @param path [String] the path string with Git-style escape sequences
|
|
42
|
+
#
|
|
43
|
+
# @return [void]
|
|
31
44
|
def initialize(path)
|
|
32
45
|
@path = path
|
|
33
46
|
end
|
|
34
47
|
|
|
35
|
-
#
|
|
48
|
+
# Converts an escaped path to an unescaped UTF-8 path
|
|
49
|
+
#
|
|
50
|
+
# @example Decode escaped path output
|
|
51
|
+
# Git::EscapedPath.new("dir/\\342\\230\\240\\n").unescape
|
|
52
|
+
# # => "dir/☠\n"
|
|
53
|
+
#
|
|
54
|
+
# @return [String] the decoded path string
|
|
36
55
|
def unescape
|
|
37
56
|
bytes = escaped_path_to_bytes(path)
|
|
38
57
|
str = bytes.pack('C*')
|
|
@@ -41,18 +60,46 @@ module Git
|
|
|
41
60
|
|
|
42
61
|
private
|
|
43
62
|
|
|
63
|
+
# Extracts an octal escape sequence starting at the given index
|
|
64
|
+
#
|
|
65
|
+
# @param path [String] the escaped path string
|
|
66
|
+
#
|
|
67
|
+
# @param index [Integer] the index where the sequence starts
|
|
68
|
+
#
|
|
69
|
+
# @return [Array(Integer, Integer)] decoded byte and consumed character count
|
|
44
70
|
def extract_octal(path, index)
|
|
45
|
-
[path[index + 1..index + 3].to_i(8), 4]
|
|
71
|
+
[path[(index + 1)..(index + 3)].to_i(8), 4]
|
|
46
72
|
end
|
|
47
73
|
|
|
74
|
+
# Extracts a single-character escape sequence at the given index
|
|
75
|
+
#
|
|
76
|
+
# @param path [String] the escaped path string
|
|
77
|
+
#
|
|
78
|
+
# @param index [Integer] the index where the sequence starts
|
|
79
|
+
#
|
|
80
|
+
# @return [Array(Integer, Integer)] decoded byte and consumed character count
|
|
48
81
|
def extract_escape(path, index)
|
|
49
82
|
[UNESCAPES[path[index + 1]], 2]
|
|
50
83
|
end
|
|
51
84
|
|
|
85
|
+
# Extracts a non-escaped character byte at the given index
|
|
86
|
+
#
|
|
87
|
+
# @param path [String] the escaped path string
|
|
88
|
+
#
|
|
89
|
+
# @param index [Integer] the index of the character to decode
|
|
90
|
+
#
|
|
91
|
+
# @return [Array(Integer, Integer)] decoded byte and consumed character count
|
|
52
92
|
def extract_single_char(path, index)
|
|
53
93
|
[path[index].ord, 1]
|
|
54
94
|
end
|
|
55
95
|
|
|
96
|
+
# Decodes the next byte from the escaped path at the given index
|
|
97
|
+
#
|
|
98
|
+
# @param path [String] the escaped path string
|
|
99
|
+
#
|
|
100
|
+
# @param index [Integer] the index to decode from
|
|
101
|
+
#
|
|
102
|
+
# @return [Array(Integer, Integer)] decoded byte and consumed character count
|
|
56
103
|
def next_byte(path, index)
|
|
57
104
|
if path[index] == '\\' && path[index + 1] >= '0' && path[index + 1] <= '7'
|
|
58
105
|
extract_octal(path, index)
|
|
@@ -63,6 +110,11 @@ module Git
|
|
|
63
110
|
end
|
|
64
111
|
end
|
|
65
112
|
|
|
113
|
+
# Converts an escaped path string into decoded bytes
|
|
114
|
+
#
|
|
115
|
+
# @param path [String] the escaped path string
|
|
116
|
+
#
|
|
117
|
+
# @return [Array<Integer>] decoded bytes in order
|
|
66
118
|
def escaped_path_to_bytes(path)
|
|
67
119
|
index = 0
|
|
68
120
|
[].tap do |bytes|
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'git/execution_context'
|
|
4
|
+
|
|
5
|
+
module Git
|
|
6
|
+
class ExecutionContext
|
|
7
|
+
# Execution context for global git commands (no repository required)
|
|
8
|
+
#
|
|
9
|
+
# Used for commands that do not require an existing repository — such as
|
|
10
|
+
# `git init`, `git clone`, and `git version`. Unlike
|
|
11
|
+
# {Git::ExecutionContext::Repository}, this class leaves `GIT_DIR`,
|
|
12
|
+
# `GIT_WORK_TREE`, and `GIT_INDEX_FILE` as `nil` (which unsets them), so
|
|
13
|
+
# that the parent environment cannot leak an unintended repository context.
|
|
14
|
+
# `GIT_SSH` is still supported to allow SSH-based remote operations
|
|
15
|
+
# (e.g. `git clone git@github.com:...`).
|
|
16
|
+
#
|
|
17
|
+
# @example Create a context using the default git binary
|
|
18
|
+
# context = Git::ExecutionContext::Global.new
|
|
19
|
+
#
|
|
20
|
+
# @example Create a context targeting a specific binary
|
|
21
|
+
# context = Git::ExecutionContext::Global.new(binary_path: '/usr/local/bin/git2')
|
|
22
|
+
#
|
|
23
|
+
# @see Git::ExecutionContext#initialize for constructor parameters and
|
|
24
|
+
# their semantics
|
|
25
|
+
#
|
|
26
|
+
# @api private
|
|
27
|
+
#
|
|
28
|
+
class Global < ExecutionContext
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|