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,144 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'git/fsck_object'
|
|
4
|
+
require 'git/fsck_result'
|
|
5
|
+
|
|
6
|
+
module Git
|
|
7
|
+
module Parsers
|
|
8
|
+
# Parser for git fsck command output
|
|
9
|
+
#
|
|
10
|
+
# Handles parsing of `git fsck` output into structured data objects
|
|
11
|
+
# for dangling, missing, unreachable objects, warnings, roots, and tagged objects.
|
|
12
|
+
#
|
|
13
|
+
# ## Design Note: Namespace Organization
|
|
14
|
+
#
|
|
15
|
+
# This parser creates and returns {Git::FsckObject} and {Git::FsckResult}
|
|
16
|
+
# objects, which live at the top-level `Git::` namespace rather than within
|
|
17
|
+
# `Git::Parsers::`. This is intentional:
|
|
18
|
+
#
|
|
19
|
+
# - **Parsers are infrastructure** - marked `@api private`, users shouldn't
|
|
20
|
+
# interact with them directly
|
|
21
|
+
# - **Result classes are public API** - returned by commands and used
|
|
22
|
+
# throughout the codebase
|
|
23
|
+
# - **FsckObject and FsckResult represent operation outcomes** - they describe
|
|
24
|
+
# repository integrity status, not parsing details
|
|
25
|
+
#
|
|
26
|
+
# Keeping these classes at `Git::` improves discoverability and correctly
|
|
27
|
+
# reflects their role as public types rather than parser internals.
|
|
28
|
+
#
|
|
29
|
+
# @api private
|
|
30
|
+
#
|
|
31
|
+
module Fsck
|
|
32
|
+
# Pattern matcher for dangling/missing/unreachable object lines
|
|
33
|
+
# Matches lines like:
|
|
34
|
+
# dangling commit abc123...
|
|
35
|
+
# missing blob def456...
|
|
36
|
+
# unreachable tree 789abc... (name)
|
|
37
|
+
OBJECT_PATTERN = /\A(dangling|missing|unreachable) (\w+) ([0-9a-f]{40})(?: \((.+)\))?\z/
|
|
38
|
+
|
|
39
|
+
# Pattern matcher for warning lines
|
|
40
|
+
# Matches lines like:
|
|
41
|
+
# warning in commit abc123...: message here
|
|
42
|
+
WARNING_PATTERN = /\Awarning in (\w+) ([0-9a-f]{40}): (.+)\z/
|
|
43
|
+
|
|
44
|
+
# Pattern matcher for root commit lines
|
|
45
|
+
# Matches lines like:
|
|
46
|
+
# root abc123...
|
|
47
|
+
ROOT_PATTERN = /\Aroot ([0-9a-f]{40})\z/
|
|
48
|
+
|
|
49
|
+
# Pattern matcher for tagged object lines
|
|
50
|
+
# Matches lines like:
|
|
51
|
+
# tagged commit abc123... (tagname) in def456...
|
|
52
|
+
TAGGED_PATTERN = /\Atagged (\w+) ([0-9a-f]{40}) \((.+)\) in ([0-9a-f]{40})\z/
|
|
53
|
+
|
|
54
|
+
module_function
|
|
55
|
+
|
|
56
|
+
# Parse git fsck output into a FsckResult object
|
|
57
|
+
#
|
|
58
|
+
# @example
|
|
59
|
+
# FsckParser.parse("dangling commit abc123...\nmissing blob def456...\n")
|
|
60
|
+
# # => #<Git::FsckResult dangling: [...], missing: [...]>
|
|
61
|
+
#
|
|
62
|
+
# @param stdout [String] output from git fsck command
|
|
63
|
+
#
|
|
64
|
+
# @return [Git::FsckResult] the parsed result
|
|
65
|
+
#
|
|
66
|
+
def parse(stdout)
|
|
67
|
+
result = { dangling: [], missing: [], unreachable: [], warnings: [], root: [], tagged: [] }
|
|
68
|
+
stdout.each_line { |line| parse_line(line.strip, result) }
|
|
69
|
+
Git::FsckResult.new(**result)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Parse a single line of fsck output
|
|
73
|
+
#
|
|
74
|
+
# @param line [String] a line of output
|
|
75
|
+
#
|
|
76
|
+
# @param result [Hash] the result hash to populate
|
|
77
|
+
#
|
|
78
|
+
# @return [Boolean] true if the line was parsed
|
|
79
|
+
#
|
|
80
|
+
def parse_line(line, result)
|
|
81
|
+
parse_object_line(line, result) ||
|
|
82
|
+
parse_warning_line(line, result) ||
|
|
83
|
+
parse_root_line(line, result) ||
|
|
84
|
+
parse_tagged_line(line, result)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Parse a dangling/missing/unreachable object line
|
|
88
|
+
#
|
|
89
|
+
# @param line [String] a line of output
|
|
90
|
+
#
|
|
91
|
+
# @param result [Hash] the result hash to populate
|
|
92
|
+
#
|
|
93
|
+
# @return [Boolean] true if the line was parsed
|
|
94
|
+
#
|
|
95
|
+
def parse_object_line(line, result)
|
|
96
|
+
return unless (match = OBJECT_PATTERN.match(line))
|
|
97
|
+
|
|
98
|
+
result[match[1].to_sym] << Git::FsckObject.new(type: match[2].to_sym, oid: match[3], name: match[4])
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Parse a warning line
|
|
102
|
+
#
|
|
103
|
+
# @param line [String] a line of output
|
|
104
|
+
#
|
|
105
|
+
# @param result [Hash] the result hash to populate
|
|
106
|
+
#
|
|
107
|
+
# @return [Boolean] true if the line was parsed
|
|
108
|
+
#
|
|
109
|
+
def parse_warning_line(line, result)
|
|
110
|
+
return unless (match = WARNING_PATTERN.match(line))
|
|
111
|
+
|
|
112
|
+
result[:warnings] << Git::FsckObject.new(type: match[1].to_sym, oid: match[2], message: match[3])
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Parse a root line
|
|
116
|
+
#
|
|
117
|
+
# @param line [String] a line of output
|
|
118
|
+
#
|
|
119
|
+
# @param result [Hash] the result hash to populate
|
|
120
|
+
#
|
|
121
|
+
# @return [Boolean] true if the line was parsed
|
|
122
|
+
#
|
|
123
|
+
def parse_root_line(line, result)
|
|
124
|
+
return unless (match = ROOT_PATTERN.match(line))
|
|
125
|
+
|
|
126
|
+
result[:root] << Git::FsckObject.new(type: :commit, oid: match[1])
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Parse a tagged line
|
|
130
|
+
#
|
|
131
|
+
# @param line [String] a line of output
|
|
132
|
+
#
|
|
133
|
+
# @param result [Hash] the result hash to populate
|
|
134
|
+
#
|
|
135
|
+
# @return [Boolean] true if the line was parsed
|
|
136
|
+
#
|
|
137
|
+
def parse_tagged_line(line, result)
|
|
138
|
+
return unless (match = TAGGED_PATTERN.match(line))
|
|
139
|
+
|
|
140
|
+
result[:tagged] << Git::FsckObject.new(type: match[1].to_sym, oid: match[2], name: match[3])
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Git
|
|
4
|
+
module Parsers
|
|
5
|
+
# Parser for `git grep` output
|
|
6
|
+
#
|
|
7
|
+
# Provides a class method that transforms raw `git grep --null` output into a
|
|
8
|
+
# structured Hash consumed by the `Git::Repository::ObjectOperations` facade.
|
|
9
|
+
#
|
|
10
|
+
# This parser is a pure text transformer with no exit-status logic. The
|
|
11
|
+
# calling facade is responsible for interpreting the command's exit status
|
|
12
|
+
# before delegating output parsing to this class.
|
|
13
|
+
#
|
|
14
|
+
# @api private
|
|
15
|
+
#
|
|
16
|
+
module Grep
|
|
17
|
+
module_function
|
|
18
|
+
|
|
19
|
+
# Parse `git grep --line-number --null --no-color` output into a match hash
|
|
20
|
+
#
|
|
21
|
+
# With `--null`, git separates the path and line number fields with NUL
|
|
22
|
+
# bytes: `treeish:filename\0linenum\0text\n`. This keeps filenames that
|
|
23
|
+
# contain `:<digits>:` from being confused with the line-number delimiter.
|
|
24
|
+
#
|
|
25
|
+
# @param output [String] raw output from `git grep --null --line-number`
|
|
26
|
+
#
|
|
27
|
+
# @return [Hash<String, Array<Array(Integer, String)>>] hash mapping
|
|
28
|
+
# `"treeish:filename"` keys to arrays of `[line_number, text]` pairs
|
|
29
|
+
#
|
|
30
|
+
# @api private
|
|
31
|
+
#
|
|
32
|
+
def parse(output)
|
|
33
|
+
output.each_line.with_object(Hash.new { |h, k| h[k] = [] }) do |line, hsh|
|
|
34
|
+
filename, line_num, text = line.chomp.split("\0", 3)
|
|
35
|
+
next unless text && line_num.match?(/\A\d+\z/)
|
|
36
|
+
|
|
37
|
+
hsh[filename] << [line_num.to_i, text]
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Git
|
|
4
|
+
module Parsers
|
|
5
|
+
# Parser for `git ls-remote` command output
|
|
6
|
+
#
|
|
7
|
+
# @api private
|
|
8
|
+
#
|
|
9
|
+
module LsRemote
|
|
10
|
+
module_function
|
|
11
|
+
|
|
12
|
+
# Parse `git ls-remote` stdout lines into a structured Hash
|
|
13
|
+
#
|
|
14
|
+
# @param lines [Array<String>] the individual stdout lines
|
|
15
|
+
#
|
|
16
|
+
# @return [Hash{String => Hash}] a map of ref type to ref data
|
|
17
|
+
#
|
|
18
|
+
# The structure differs by key:
|
|
19
|
+
#
|
|
20
|
+
# - `"head"` maps directly to `{ ref: String, sha: String }`
|
|
21
|
+
# - Other keys (e.g. `"branches"`, `"tags"`) map to
|
|
22
|
+
# `{ name => { ref: String, sha: String } }`
|
|
23
|
+
#
|
|
24
|
+
def parse_output(lines)
|
|
25
|
+
lines.each_with_object(Hash.new { |h, k| h[k] = {} }) do |line, hsh|
|
|
26
|
+
type, name, value = parse_line(line)
|
|
27
|
+
if name
|
|
28
|
+
hsh[type][name] = value
|
|
29
|
+
else
|
|
30
|
+
hsh[type].update(value)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Parse a single `git ls-remote` output line
|
|
36
|
+
#
|
|
37
|
+
# @param line [String] a single line from ls-remote stdout
|
|
38
|
+
#
|
|
39
|
+
# @return [Array(String, (String, nil), Hash)] `[type, name, value]` where
|
|
40
|
+
# `value` is `{ ref: String, sha: String }`
|
|
41
|
+
#
|
|
42
|
+
# @raise [Git::UnexpectedResultError] if the line is not in `<sha>\t<ref>` format
|
|
43
|
+
#
|
|
44
|
+
def parse_line(line)
|
|
45
|
+
unless line.include?("\t") && line.match?(/\A[0-9a-f]{4,}\t/)
|
|
46
|
+
raise Git::UnexpectedResultError, "Unexpected ls-remote output line: #{line.inspect}"
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
sha, info = line.split("\t", 2)
|
|
50
|
+
_, type, name = info.split('/', 3)
|
|
51
|
+
|
|
52
|
+
type ||= 'head'
|
|
53
|
+
type = 'branches' if type == 'heads'
|
|
54
|
+
|
|
55
|
+
value = { ref: info, sha: sha }
|
|
56
|
+
|
|
57
|
+
[type, name, value]
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Parse `git ls-remote --symref <repo> HEAD` output into a branch name
|
|
61
|
+
#
|
|
62
|
+
# @param output [String] command stdout
|
|
63
|
+
#
|
|
64
|
+
# @return [String] the default branch name
|
|
65
|
+
#
|
|
66
|
+
# @raise [Git::UnexpectedResultError] when the branch cannot be determined
|
|
67
|
+
#
|
|
68
|
+
def parse_default_branch(output)
|
|
69
|
+
match_data = output.match(%r{^ref: refs/remotes/[^/]+/(?<default_branch>[^\t]+)\t})
|
|
70
|
+
return match_data[:default_branch] if match_data
|
|
71
|
+
|
|
72
|
+
match_data = output.match(%r{^ref: refs/heads/(?<default_branch>[^\t]+)\tHEAD$})
|
|
73
|
+
return match_data[:default_branch] if match_data
|
|
74
|
+
|
|
75
|
+
raise Git::UnexpectedResultError, 'Unable to determine the default branch'
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'git/escaped_path'
|
|
4
|
+
|
|
5
|
+
module Git
|
|
6
|
+
module Parsers
|
|
7
|
+
# Parser for `git ls-tree` output
|
|
8
|
+
#
|
|
9
|
+
# Provides a class method that transforms raw `git ls-tree` output into a
|
|
10
|
+
# structured Hash consumed by the `Git::Repository::ObjectOperations` facade.
|
|
11
|
+
#
|
|
12
|
+
# @api private
|
|
13
|
+
#
|
|
14
|
+
module LsTree
|
|
15
|
+
module_function
|
|
16
|
+
|
|
17
|
+
# Parse `git ls-tree` output into a type-keyed hash of entries
|
|
18
|
+
#
|
|
19
|
+
# Each line of output is expected in the format produced by
|
|
20
|
+
# `git ls-tree`: `<mode> <type> <sha>\t<file>`.
|
|
21
|
+
#
|
|
22
|
+
# @param output [String] raw stdout from `git ls-tree`
|
|
23
|
+
#
|
|
24
|
+
# @return [Hash<String, Hash<String, Hash>>] hash keyed by object type
|
|
25
|
+
# (`'blob'`, `'tree'`, `'commit'`), then by filename, holding
|
|
26
|
+
# `:mode` and `:sha` values
|
|
27
|
+
#
|
|
28
|
+
# @api private
|
|
29
|
+
#
|
|
30
|
+
def parse(output)
|
|
31
|
+
data = { 'blob' => {}, 'tree' => {}, 'commit' => {} }
|
|
32
|
+
output.split("\n").each do |line|
|
|
33
|
+
info, filenm = line.split("\t", 2)
|
|
34
|
+
filenm = unescape_path(filenm) if filenm
|
|
35
|
+
mode, type, entry_sha = info.split
|
|
36
|
+
data[type][filenm] = { mode: mode, sha: entry_sha }
|
|
37
|
+
end
|
|
38
|
+
data
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Converts a git-quoted path back to its original form
|
|
42
|
+
#
|
|
43
|
+
# @param path [String] the path, possibly git-quoted
|
|
44
|
+
#
|
|
45
|
+
# @return [String] the unquoted path
|
|
46
|
+
#
|
|
47
|
+
# @api private
|
|
48
|
+
#
|
|
49
|
+
def unescape_path(path)
|
|
50
|
+
if path.start_with?('"') && path.end_with?('"')
|
|
51
|
+
Git::EscapedPath.new(path[1..-2]).unescape
|
|
52
|
+
else
|
|
53
|
+
path
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'git/config_entry_info'
|
|
4
|
+
require 'git/remote_info'
|
|
5
|
+
|
|
6
|
+
module Git
|
|
7
|
+
module Parsers
|
|
8
|
+
# Parser that builds {Git::RemoteInfo} objects from git config entries
|
|
9
|
+
#
|
|
10
|
+
# Accepts `Array<Git::ConfigEntryInfo>` as returned by
|
|
11
|
+
# {Git::Configuring#config_list} and groups entries by remote name,
|
|
12
|
+
# collecting multi-value fields and coercing boolean values.
|
|
13
|
+
#
|
|
14
|
+
# @api private
|
|
15
|
+
#
|
|
16
|
+
module Remote
|
|
17
|
+
module_function
|
|
18
|
+
|
|
19
|
+
# Parse an array of config entries into an array of RemoteInfo objects
|
|
20
|
+
#
|
|
21
|
+
# Groups entries whose key matches `remote.<name>.<variable>` by remote
|
|
22
|
+
# name and builds one {Git::RemoteInfo} per unique remote name.
|
|
23
|
+
#
|
|
24
|
+
# Non-remote config entries (keys that do not start with `remote.`) are
|
|
25
|
+
# silently ignored.
|
|
26
|
+
#
|
|
27
|
+
# @param config_entries [Array<Git::ConfigEntryInfo>] all config entries
|
|
28
|
+
# to inspect; may contain non-remote entries
|
|
29
|
+
#
|
|
30
|
+
# @return [Array<Git::RemoteInfo>] one entry per unique remote name, in
|
|
31
|
+
# the order the first entry for each name appeared in `config_entries`
|
|
32
|
+
#
|
|
33
|
+
# @raise [ArgumentError] if a boolean config field carries an unrecognized
|
|
34
|
+
# value (mirrors git's own fatal-error behavior)
|
|
35
|
+
#
|
|
36
|
+
def parse_list(config_entries)
|
|
37
|
+
group_by_remote(config_entries).map { |name, pairs| build_remote_info(name, pairs) }
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Group config entries by remote name, collecting variable/value pairs
|
|
41
|
+
#
|
|
42
|
+
# @param config_entries [Array<Git::ConfigEntryInfo>]
|
|
43
|
+
#
|
|
44
|
+
# @return [Hash{String => Array}] remote name to variable/value pairs
|
|
45
|
+
#
|
|
46
|
+
# @api private
|
|
47
|
+
#
|
|
48
|
+
def group_by_remote(config_entries)
|
|
49
|
+
config_entries.each_with_object({}) do |entry, groups|
|
|
50
|
+
next unless entry.section == 'remote' && !entry.subsection.empty?
|
|
51
|
+
|
|
52
|
+
remote_name = entry.subsection
|
|
53
|
+
variable = entry.variable.downcase
|
|
54
|
+
groups[remote_name] ||= []
|
|
55
|
+
groups[remote_name] << [variable, entry.value]
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Config variable names that hold arrays of strings
|
|
60
|
+
ARRAY_VARIABLES = %w[url pushurl fetch push].freeze
|
|
61
|
+
private_constant :ARRAY_VARIABLES
|
|
62
|
+
|
|
63
|
+
# Config variable names that hold boolean values
|
|
64
|
+
BOOLEAN_VARIABLES = %w[mirror skipdefaultupdate prune prunetags promisor].freeze
|
|
65
|
+
private_constant :BOOLEAN_VARIABLES
|
|
66
|
+
|
|
67
|
+
# Map from git config variable name to RemoteInfo field name (symbol)
|
|
68
|
+
#
|
|
69
|
+
# All keys are lowercase because `git config --list` unconditionally
|
|
70
|
+
# lowercases section names and variable names (subsection names are
|
|
71
|
+
# case-preserved, but are never used as lookup keys here).
|
|
72
|
+
VARIABLE_TO_FIELD = {
|
|
73
|
+
'url' => :url,
|
|
74
|
+
'pushurl' => :push_url,
|
|
75
|
+
'fetch' => :fetch,
|
|
76
|
+
'push' => :push,
|
|
77
|
+
'mirror' => :mirror,
|
|
78
|
+
'skipdefaultupdate' => :skip_default_update,
|
|
79
|
+
'tagopt' => :tag_opt,
|
|
80
|
+
'prune' => :prune,
|
|
81
|
+
'prunetags' => :prune_tags,
|
|
82
|
+
'receivepack' => :receivepack,
|
|
83
|
+
'uploadpack' => :uploadpack,
|
|
84
|
+
'promisor' => :promisor,
|
|
85
|
+
'partialclonefilter' => :partial_clone_filter,
|
|
86
|
+
'vcs' => :vcs
|
|
87
|
+
}.freeze
|
|
88
|
+
private_constant :VARIABLE_TO_FIELD
|
|
89
|
+
|
|
90
|
+
# True string values per git's boolean rules
|
|
91
|
+
BOOL_TRUE_VALUES = %w[true yes on 1].freeze
|
|
92
|
+
private_constant :BOOL_TRUE_VALUES
|
|
93
|
+
|
|
94
|
+
# False string values per git's boolean rules
|
|
95
|
+
BOOL_FALSE_VALUES = %w[false no off 0].freeze
|
|
96
|
+
private_constant :BOOL_FALSE_VALUES
|
|
97
|
+
|
|
98
|
+
# Build a RemoteInfo from grouped variable/value pairs
|
|
99
|
+
#
|
|
100
|
+
# @param remote_name [String]
|
|
101
|
+
#
|
|
102
|
+
# @param pairs [Array] variable/value pairs collected from config entries
|
|
103
|
+
#
|
|
104
|
+
# @return [Git::RemoteInfo]
|
|
105
|
+
#
|
|
106
|
+
# @api private
|
|
107
|
+
#
|
|
108
|
+
def build_remote_info(remote_name, pairs)
|
|
109
|
+
attrs = { name: remote_name }
|
|
110
|
+
pairs.each { |variable, value| apply_pair(attrs, variable, value) }
|
|
111
|
+
Git::RemoteInfo.new(**attrs)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# Apply a single variable/value pair to an attrs hash
|
|
115
|
+
#
|
|
116
|
+
# @param attrs [Hash] accumulator of RemoteInfo keyword arguments
|
|
117
|
+
#
|
|
118
|
+
# @param variable [String] git config variable name (e.g. 'url', 'tagopt')
|
|
119
|
+
#
|
|
120
|
+
# @param value [String] raw config value
|
|
121
|
+
#
|
|
122
|
+
# @return [void]
|
|
123
|
+
#
|
|
124
|
+
# @api private
|
|
125
|
+
#
|
|
126
|
+
def apply_pair(attrs, variable, value)
|
|
127
|
+
field = VARIABLE_TO_FIELD[variable]
|
|
128
|
+
return unless field # ignore unknown variables
|
|
129
|
+
|
|
130
|
+
if ARRAY_VARIABLES.include?(variable)
|
|
131
|
+
attrs[field] ||= []
|
|
132
|
+
attrs[field] << value
|
|
133
|
+
elsif BOOLEAN_VARIABLES.include?(variable)
|
|
134
|
+
attrs[field] = coerce_boolean(variable, value)
|
|
135
|
+
else
|
|
136
|
+
attrs[field] = value
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# Coerce a git config boolean string to a Ruby boolean
|
|
141
|
+
#
|
|
142
|
+
# @param variable [String] the config variable name (for error messages)
|
|
143
|
+
#
|
|
144
|
+
# @param value [String] the raw string value from git config
|
|
145
|
+
#
|
|
146
|
+
# @return [Boolean] `true` or `false`
|
|
147
|
+
#
|
|
148
|
+
# @raise [ArgumentError] if the value is not a recognized boolean
|
|
149
|
+
#
|
|
150
|
+
# @api private
|
|
151
|
+
#
|
|
152
|
+
def coerce_boolean(variable, value)
|
|
153
|
+
normalized = value.downcase
|
|
154
|
+
return true if BOOL_TRUE_VALUES.include?(normalized) || normalized == ''
|
|
155
|
+
return false if BOOL_FALSE_VALUES.include?(normalized)
|
|
156
|
+
|
|
157
|
+
raise ArgumentError,
|
|
158
|
+
"unrecognized boolean value #{value.inspect} for config variable #{variable.inspect}"
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
end
|