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,559 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'logger'
|
|
4
|
+
require 'git/command_line'
|
|
5
|
+
|
|
6
|
+
module Git
|
|
7
|
+
# Base class for execution contexts that run git commands
|
|
8
|
+
#
|
|
9
|
+
# An execution context bundles three concerns that together describe *how* and
|
|
10
|
+
# *where* a git command runs:
|
|
11
|
+
#
|
|
12
|
+
# 1. **Repository scope** — the public accessors `git_dir`, `git_work_dir`,
|
|
13
|
+
# `git_index_file`, and `git_ssh` identify which repository git targets and
|
|
14
|
+
# which SSH wrapper to use. Their values are translated into `GIT_*` environment
|
|
15
|
+
# variable overrides by the private `env_overrides` method. A `nil` value
|
|
16
|
+
# unsets the variable (see `Process.spawn` semantics).
|
|
17
|
+
#
|
|
18
|
+
# 2. **CLI global options** — the private `global_opts` method returns the array
|
|
19
|
+
# of git flags prepended to every invocation: `--git-dir` / `--work-tree` when
|
|
20
|
+
# those attributes are set, plus the static options in {STATIC_GLOBAL_OPTS} that
|
|
21
|
+
# ensure deterministic, script-friendly output.
|
|
22
|
+
#
|
|
23
|
+
# 3. **Execution defaults** — {COMMAND_CAPTURING_ARG_DEFAULTS} and
|
|
24
|
+
# {COMMAND_STREAMING_ARG_DEFAULTS} define the default values for I/O, encoding,
|
|
25
|
+
# and behavioral options (`in:`, `out:`, `normalize:`, `timeout:`, etc.) accepted
|
|
26
|
+
# by {#command_capturing} and {#command_streaming}.
|
|
27
|
+
#
|
|
28
|
+
# Subclasses override the repository-scope accessors to supply context-specific
|
|
29
|
+
# values. The `env_overrides` and `global_opts` methods are implemented here and
|
|
30
|
+
# call those accessors, so subclasses do not need to override them directly.
|
|
31
|
+
#
|
|
32
|
+
# Concrete subclasses:
|
|
33
|
+
# - {Git::ExecutionContext::Repository} — for repository-bound commands (`add`, `commit`, …)
|
|
34
|
+
# - {Git::ExecutionContext::Global} — for commands that do not require an existing repository
|
|
35
|
+
# (`init`, `clone`, `version`)
|
|
36
|
+
#
|
|
37
|
+
# @example Using a concrete subclass
|
|
38
|
+
# context = Git::ExecutionContext::Global.new(binary_path: '/usr/local/bin/git2')
|
|
39
|
+
# context.binary_path #=> "/usr/local/bin/git2"
|
|
40
|
+
#
|
|
41
|
+
# @api private
|
|
42
|
+
#
|
|
43
|
+
class ExecutionContext
|
|
44
|
+
# Default keyword arguments accepted by {#command_capturing}.
|
|
45
|
+
#
|
|
46
|
+
# Derived from {Git::CommandLine::Capturing::RUN_OPTION_DEFAULTS} with two
|
|
47
|
+
# overrides: `normalize: true` and `chomp: true` so callers receive clean
|
|
48
|
+
# UTF-8 strings by default. New options added to the CommandLine layer are
|
|
49
|
+
# automatically accepted here without requiring a coordinated edit.
|
|
50
|
+
#
|
|
51
|
+
# `timeout: nil` is intentional — the global timeout from {Git.config} is
|
|
52
|
+
# applied at call-time so that changes to the config are respected.
|
|
53
|
+
#
|
|
54
|
+
COMMAND_CAPTURING_ARG_DEFAULTS =
|
|
55
|
+
Git::CommandLine::Capturing::RUN_OPTION_DEFAULTS
|
|
56
|
+
.merge(normalize: true, chomp: true)
|
|
57
|
+
.freeze
|
|
58
|
+
|
|
59
|
+
# Default keyword arguments accepted by {#command_streaming}.
|
|
60
|
+
#
|
|
61
|
+
# Identical to {Git::CommandLine::Streaming::RUN_OPTION_DEFAULTS}. Defined
|
|
62
|
+
# here so callers interact with a stable constant on this class, and so that
|
|
63
|
+
# new options added to the CommandLine layer are automatically accepted.
|
|
64
|
+
#
|
|
65
|
+
COMMAND_STREAMING_ARG_DEFAULTS =
|
|
66
|
+
Git::CommandLine::Streaming::RUN_OPTION_DEFAULTS.dup.freeze
|
|
67
|
+
|
|
68
|
+
# Static git global options applied to every invocation.
|
|
69
|
+
#
|
|
70
|
+
# These ensure deterministic, script-friendly output regardless of the
|
|
71
|
+
# user's local git configuration.
|
|
72
|
+
#
|
|
73
|
+
STATIC_GLOBAL_OPTS = %w[
|
|
74
|
+
-c core.quotePath=true
|
|
75
|
+
-c core.editor=false
|
|
76
|
+
-c color.ui=false
|
|
77
|
+
-c color.advice=false
|
|
78
|
+
-c color.diff=false
|
|
79
|
+
-c color.grep=false
|
|
80
|
+
-c color.push=false
|
|
81
|
+
-c color.remote=false
|
|
82
|
+
-c color.showBranch=false
|
|
83
|
+
-c color.status=false
|
|
84
|
+
-c color.transport=false
|
|
85
|
+
].freeze
|
|
86
|
+
|
|
87
|
+
# Creates a new execution context
|
|
88
|
+
#
|
|
89
|
+
# @param binary_path [String, :use_global_config] path to the git binary
|
|
90
|
+
#
|
|
91
|
+
# Give `:use_global_config` (the default) to use `Git::Config.instance.binary_path`.
|
|
92
|
+
#
|
|
93
|
+
# Passing `nil` raises `ArgumentError` — there is no "unset the
|
|
94
|
+
# binary" semantic.
|
|
95
|
+
#
|
|
96
|
+
# @param git_ssh [String, nil, :use_global_config] the SSH wrapper path
|
|
97
|
+
#
|
|
98
|
+
# Give `nil` to unset `GIT_SSH`, or `:use_global_config` (default)
|
|
99
|
+
# to use `Git::Config.instance.git_ssh`.
|
|
100
|
+
#
|
|
101
|
+
# @param logger [Logger, nil] the logger to use in the CommandLine layer
|
|
102
|
+
#
|
|
103
|
+
# Give `nil` to use a null logger (`Logger.new(nil)`).
|
|
104
|
+
#
|
|
105
|
+
# @raise [NotImplementedError] if called directly on {Git::ExecutionContext}
|
|
106
|
+
# rather than a subclass
|
|
107
|
+
#
|
|
108
|
+
# @raise [ArgumentError] if `binary_path` is `nil`
|
|
109
|
+
#
|
|
110
|
+
def initialize(binary_path: :use_global_config, git_ssh: :use_global_config, logger: nil)
|
|
111
|
+
if instance_of?(Git::ExecutionContext)
|
|
112
|
+
raise NotImplementedError, 'Git::ExecutionContext is an abstract base class'
|
|
113
|
+
end
|
|
114
|
+
raise ArgumentError, 'binary_path must not be nil' if binary_path.nil?
|
|
115
|
+
|
|
116
|
+
@binary_path = binary_path
|
|
117
|
+
@git_ssh = git_ssh
|
|
118
|
+
@logger = logger || Logger.new(nil)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Returns the `GIT_DIR` path for this context
|
|
122
|
+
#
|
|
123
|
+
# `nil` means `GIT_DIR` will be explicitly **unset** in the child process
|
|
124
|
+
# (per `Process.spawn` semantics — unset is not the same as inherited).
|
|
125
|
+
# Subclasses override this to supply a repository-specific path.
|
|
126
|
+
#
|
|
127
|
+
# @example Base class returns nil; subclasses return the actual path
|
|
128
|
+
# context = Git::ExecutionContext::Global.new
|
|
129
|
+
# context.git_dir #=> nil
|
|
130
|
+
#
|
|
131
|
+
# @return [String, nil] the `GIT_DIR` path, or `nil` to unset the variable
|
|
132
|
+
#
|
|
133
|
+
def git_dir = nil
|
|
134
|
+
|
|
135
|
+
# Returns the `GIT_WORK_TREE` path for this context
|
|
136
|
+
#
|
|
137
|
+
# `nil` means `GIT_WORK_TREE` will be explicitly **unset** in the child process.
|
|
138
|
+
#
|
|
139
|
+
# @example Base class returns nil; subclasses return the actual path
|
|
140
|
+
# context = Git::ExecutionContext::Global.new
|
|
141
|
+
# context.git_work_dir #=> nil
|
|
142
|
+
#
|
|
143
|
+
# @return [String, nil] the `GIT_WORK_TREE` path, or `nil` to unset the variable
|
|
144
|
+
#
|
|
145
|
+
def git_work_dir = nil
|
|
146
|
+
|
|
147
|
+
# Returns the `GIT_INDEX_FILE` path for this context
|
|
148
|
+
#
|
|
149
|
+
# `nil` means `GIT_INDEX_FILE` will be explicitly **unset** in the child process.
|
|
150
|
+
#
|
|
151
|
+
# @example Base class returns nil; subclasses return the actual path
|
|
152
|
+
# context = Git::ExecutionContext::Global.new
|
|
153
|
+
# context.git_index_file #=> nil
|
|
154
|
+
#
|
|
155
|
+
# @return [String, nil] the `GIT_INDEX_FILE` path, or `nil` to unset the variable
|
|
156
|
+
#
|
|
157
|
+
def git_index_file = nil
|
|
158
|
+
|
|
159
|
+
# Returns the resolved git binary path for this context
|
|
160
|
+
#
|
|
161
|
+
# `:use_global_config` is resolved to `Git::Config.instance.binary_path` each time a
|
|
162
|
+
# command method is called, so runtime changes to
|
|
163
|
+
# `Git.configure { |c| c.binary_path = ... }`
|
|
164
|
+
# are reflected per command invocation.
|
|
165
|
+
#
|
|
166
|
+
# @example With the default sentinel (resolves from Git::Config.instance at call-time)
|
|
167
|
+
# context = Git::ExecutionContext::Global.new
|
|
168
|
+
# context.binary_path #=> "git"
|
|
169
|
+
#
|
|
170
|
+
# @example With an explicit path
|
|
171
|
+
# context = Git::ExecutionContext::Global.new(binary_path: '/usr/local/bin/git2')
|
|
172
|
+
# context.binary_path #=> "/usr/local/bin/git2"
|
|
173
|
+
#
|
|
174
|
+
# @return [String] the resolved git binary path
|
|
175
|
+
#
|
|
176
|
+
def binary_path
|
|
177
|
+
return Git::Config.instance.binary_path if @binary_path == :use_global_config
|
|
178
|
+
|
|
179
|
+
@binary_path
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
# Returns the resolved `GIT_SSH` wrapper path for this context
|
|
183
|
+
#
|
|
184
|
+
# `:use_global_config` is resolved to `Git::Config.instance.git_ssh` each time a
|
|
185
|
+
# command method is called, so runtime changes to
|
|
186
|
+
# `Git.configure { |c| c.git_ssh = ... }`
|
|
187
|
+
# are reflected per command invocation. `nil` means the variable will be
|
|
188
|
+
# explicitly unset.
|
|
189
|
+
#
|
|
190
|
+
# @example With the default sentinel (resolves from Git::Config.instance at call-time)
|
|
191
|
+
# context = Git::ExecutionContext::Global.new
|
|
192
|
+
# context.git_ssh #=> nil
|
|
193
|
+
#
|
|
194
|
+
# @example With an explicit path
|
|
195
|
+
# context = Git::ExecutionContext::Global.new(git_ssh: '/usr/bin/ssh-wrapper')
|
|
196
|
+
# context.git_ssh #=> "/usr/bin/ssh-wrapper"
|
|
197
|
+
#
|
|
198
|
+
# @return [String, nil] the resolved `GIT_SSH` wrapper path, or `nil` to unset
|
|
199
|
+
#
|
|
200
|
+
def git_ssh
|
|
201
|
+
return Git::Config.instance.git_ssh if @git_ssh == :use_global_config
|
|
202
|
+
|
|
203
|
+
@git_ssh
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
# Returns the logger used by this context
|
|
207
|
+
#
|
|
208
|
+
# @example
|
|
209
|
+
# context = Git::ExecutionContext::Repository.new(git_dir: '/repo/.git', logger: my_logger)
|
|
210
|
+
# context.logger #=> my_logger
|
|
211
|
+
#
|
|
212
|
+
# @return [Logger] the logger instance; never `nil`
|
|
213
|
+
#
|
|
214
|
+
# @api private
|
|
215
|
+
#
|
|
216
|
+
attr_reader :logger
|
|
217
|
+
|
|
218
|
+
# Runs a git command and returns the result
|
|
219
|
+
#
|
|
220
|
+
# By default, raises {Git::FailedError} if the command exits with a non-zero
|
|
221
|
+
# status. Pass `raise_on_failure: false` to suppress this behavior.
|
|
222
|
+
#
|
|
223
|
+
# @overload command_capturing(*args, **options_hash)
|
|
224
|
+
#
|
|
225
|
+
# Args should exclude the 'git' command itself and global options. Remember to
|
|
226
|
+
# splat the arguments if given as an array.
|
|
227
|
+
#
|
|
228
|
+
# @example Run git log
|
|
229
|
+
# result = command_capturing('log', '--pretty=oneline')
|
|
230
|
+
# result.stdout #=> "abc123 First commit\ndef456 Second commit\n"
|
|
231
|
+
#
|
|
232
|
+
# @example Using an array of arguments
|
|
233
|
+
# args = ['log', '--pretty=oneline']
|
|
234
|
+
# result = command_capturing(*args)
|
|
235
|
+
#
|
|
236
|
+
# @example Suppress raising on failure
|
|
237
|
+
# result = command_capturing('show', 'nonexistent', raise_on_failure: false)
|
|
238
|
+
# result.status.success? #=> false
|
|
239
|
+
#
|
|
240
|
+
# @param args [Array<String>] the command and its arguments
|
|
241
|
+
#
|
|
242
|
+
# @return [Git::CommandLine::Result] the result of the command
|
|
243
|
+
#
|
|
244
|
+
# @param options_hash [Hash] the options to pass to the command
|
|
245
|
+
#
|
|
246
|
+
# @option options_hash [IO, nil] :in the IO object to use as stdin, or nil to
|
|
247
|
+
# inherit the parent process stdin
|
|
248
|
+
#
|
|
249
|
+
# Must be a real IO object with a file descriptor.
|
|
250
|
+
#
|
|
251
|
+
# @option options_hash [IO, String, #write, nil] :out the destination for
|
|
252
|
+
# captured stdout
|
|
253
|
+
#
|
|
254
|
+
# @option options_hash [IO, String, #write, nil] :err the destination for
|
|
255
|
+
# captured stderr
|
|
256
|
+
#
|
|
257
|
+
# @option options_hash [Boolean, nil] :normalize (true) normalize the output
|
|
258
|
+
# encoding to UTF-8
|
|
259
|
+
#
|
|
260
|
+
# @option options_hash [Boolean, nil] :chomp (true) remove trailing newlines
|
|
261
|
+
# from the output
|
|
262
|
+
#
|
|
263
|
+
# @option options_hash [Boolean, nil] :merge (false) merge stdout and stderr
|
|
264
|
+
# into a single output
|
|
265
|
+
#
|
|
266
|
+
# @option options_hash [String, nil] :chdir the directory to run the command in
|
|
267
|
+
#
|
|
268
|
+
# @option options_hash [Hash] :env additional environment variable overrides
|
|
269
|
+
# for this command
|
|
270
|
+
#
|
|
271
|
+
# @option options_hash [Boolean, nil] :raise_on_failure (true) whether to raise on
|
|
272
|
+
# non-zero exit
|
|
273
|
+
#
|
|
274
|
+
# @option options_hash [Numeric, nil] :timeout the maximum seconds to wait for
|
|
275
|
+
# the command to complete
|
|
276
|
+
#
|
|
277
|
+
# If timeout is nil, the global timeout from {Git::Config} is used.
|
|
278
|
+
#
|
|
279
|
+
# If timeout is zero, the timeout will not be enforced.
|
|
280
|
+
#
|
|
281
|
+
# If the command times out, it is killed via a `SIGKILL` signal and
|
|
282
|
+
# `Git::TimeoutError` is raised.
|
|
283
|
+
#
|
|
284
|
+
# If the command does not respond to SIGKILL, it will hang this method.
|
|
285
|
+
#
|
|
286
|
+
# @raise [ArgumentError] if an unknown option is passed
|
|
287
|
+
#
|
|
288
|
+
# @raise [Git::FailedError] if the command failed (when raise_on_failure is
|
|
289
|
+
# true)
|
|
290
|
+
#
|
|
291
|
+
# @raise [Git::SignaledError] if the command was signaled
|
|
292
|
+
#
|
|
293
|
+
# @raise [Git::TimeoutError] if the command times out
|
|
294
|
+
#
|
|
295
|
+
# @raise [Git::ProcessIOError] if an exception was raised while collecting
|
|
296
|
+
# subprocess output
|
|
297
|
+
#
|
|
298
|
+
# The exception's `result` attribute is a {Git::CommandLine::Result} which will
|
|
299
|
+
# contain the result of the command including the exit status, stdout, and stderr.
|
|
300
|
+
#
|
|
301
|
+
# @note Individual command classes (under {Git::Commands}) can selectively expose
|
|
302
|
+
# `:timeout` and `:env` and other options to their callers by declaring them as
|
|
303
|
+
# execution options in their Arguments DSL definition and forwarding them to
|
|
304
|
+
# this method. See {Git::Commands::Clone#call} for an example of a command that
|
|
305
|
+
# exposes `:timeout`.
|
|
306
|
+
#
|
|
307
|
+
# @see Git::CommandLine::Capturing#run
|
|
308
|
+
#
|
|
309
|
+
def command_capturing(*, **options_hash)
|
|
310
|
+
options_hash = COMMAND_CAPTURING_ARG_DEFAULTS.merge(options_hash)
|
|
311
|
+
options_hash[:timeout] ||= Git.config.timeout
|
|
312
|
+
|
|
313
|
+
extra_options = options_hash.keys - COMMAND_CAPTURING_ARG_DEFAULTS.keys
|
|
314
|
+
raise ArgumentError, "Unknown options: #{extra_options.join(', ')}" if extra_options.any?
|
|
315
|
+
|
|
316
|
+
env = options_hash.delete(:env)
|
|
317
|
+
raise_on_failure = options_hash.delete(:raise_on_failure)
|
|
318
|
+
command_line_capturing.run(*, raise_on_failure: raise_on_failure, env: env, **options_hash)
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
# Runs a git command using the streaming (non-capturing) execution path
|
|
322
|
+
#
|
|
323
|
+
# Unlike {#command_capturing}, stdout is NOT buffered in memory. It is
|
|
324
|
+
# written only to the IO object provided via the `out:` option. Stderr is
|
|
325
|
+
# captured internally via a StringIO for error diagnostics.
|
|
326
|
+
#
|
|
327
|
+
# Use this entry point when you want to stream large output (e.g. blob
|
|
328
|
+
# content from cat-file) without creating memory pressure.
|
|
329
|
+
#
|
|
330
|
+
# @overload command_streaming(*args, **options_hash)
|
|
331
|
+
#
|
|
332
|
+
# Streams a git command's output to the provided IO object
|
|
333
|
+
#
|
|
334
|
+
# @example Stream blob content to a file
|
|
335
|
+
# File.open('blob.bin', 'wb') do |f|
|
|
336
|
+
# command_streaming('cat-file', 'blob', 'HEAD:large_file.bin', out: f)
|
|
337
|
+
# end
|
|
338
|
+
#
|
|
339
|
+
# @param args [Array<String>] the git command and its arguments
|
|
340
|
+
#
|
|
341
|
+
# @return [Git::CommandLine::Result] the result of the command
|
|
342
|
+
#
|
|
343
|
+
# @param options_hash [Hash] the options to pass to the command
|
|
344
|
+
#
|
|
345
|
+
# @option options_hash [IO, nil] :in the IO object to use as stdin, or nil to
|
|
346
|
+
# inherit the parent process stdin
|
|
347
|
+
#
|
|
348
|
+
# Must be a real IO object with a file descriptor.
|
|
349
|
+
#
|
|
350
|
+
# @option options_hash [#write, nil] :out destination for streamed stdout
|
|
351
|
+
#
|
|
352
|
+
# @option options_hash [#write, nil] :err an optional additional destination
|
|
353
|
+
# to receive stderr output in real time
|
|
354
|
+
#
|
|
355
|
+
# Stderr is always captured internally; when `err:` is supplied, writes are
|
|
356
|
+
# teed to both the internal buffer and this destination. `result.stderr`
|
|
357
|
+
# always reflects the internal capture.
|
|
358
|
+
#
|
|
359
|
+
# @option options_hash [String, nil] :chdir the directory to run the command in
|
|
360
|
+
#
|
|
361
|
+
# @option options_hash [Hash] :env additional environment variable overrides
|
|
362
|
+
# for this command
|
|
363
|
+
#
|
|
364
|
+
# @option options_hash [Boolean, nil] :raise_on_failure (true) whether to raise on
|
|
365
|
+
# non-zero exit
|
|
366
|
+
#
|
|
367
|
+
# @option options_hash [Numeric, nil] :timeout the maximum seconds to wait for
|
|
368
|
+
# the command to complete
|
|
369
|
+
#
|
|
370
|
+
# If timeout is nil, the global timeout from {Git::Config} is used.
|
|
371
|
+
#
|
|
372
|
+
# If timeout is zero, the timeout will not be enforced.
|
|
373
|
+
#
|
|
374
|
+
# If the command times out, it is killed via a `SIGKILL` signal and
|
|
375
|
+
# `Git::TimeoutError` is raised.
|
|
376
|
+
#
|
|
377
|
+
# If the command does not respond to SIGKILL, it will hang this method.
|
|
378
|
+
#
|
|
379
|
+
# `result.stdout` will always be `''` — stdout was streamed to `out:`.
|
|
380
|
+
#
|
|
381
|
+
# `result.stderr` contains any stderr output captured for diagnostics.
|
|
382
|
+
#
|
|
383
|
+
# @raise [ArgumentError] if an unknown option is passed
|
|
384
|
+
#
|
|
385
|
+
# @raise [Git::FailedError] if the command failed (when raise_on_failure is true)
|
|
386
|
+
#
|
|
387
|
+
# @raise [Git::SignaledError] if the command was signaled
|
|
388
|
+
#
|
|
389
|
+
# @raise [Git::TimeoutError] if the command times out
|
|
390
|
+
#
|
|
391
|
+
# @raise [Git::ProcessIOError] if an exception was raised while collecting
|
|
392
|
+
# subprocess output
|
|
393
|
+
#
|
|
394
|
+
# @see Git::CommandLine::Streaming#run
|
|
395
|
+
#
|
|
396
|
+
def command_streaming(*, **options_hash)
|
|
397
|
+
options_hash = COMMAND_STREAMING_ARG_DEFAULTS.merge(options_hash)
|
|
398
|
+
options_hash[:timeout] ||= Git.config.timeout
|
|
399
|
+
|
|
400
|
+
extra_options = options_hash.keys - COMMAND_STREAMING_ARG_DEFAULTS.keys
|
|
401
|
+
raise ArgumentError, "Unknown options: #{extra_options.join(', ')}" if extra_options.any?
|
|
402
|
+
|
|
403
|
+
env = options_hash.delete(:env)
|
|
404
|
+
raise_on_failure = options_hash.delete(:raise_on_failure)
|
|
405
|
+
command_line_streaming.run(*, raise_on_failure: raise_on_failure, env: env, **options_hash)
|
|
406
|
+
end
|
|
407
|
+
|
|
408
|
+
# Returns the installed git version
|
|
409
|
+
#
|
|
410
|
+
# The result is memoized per instance. Accepts an optional timeout used
|
|
411
|
+
# only when the version has not yet been fetched for this context.
|
|
412
|
+
#
|
|
413
|
+
# @param timeout [Numeric, nil] seconds to wait for `git version`; `nil`
|
|
414
|
+
# falls back to the global {Git::Config} timeout; `0` disables the timeout
|
|
415
|
+
# entirely (the command runs until it completes or the process is killed)
|
|
416
|
+
#
|
|
417
|
+
# @return [Git::Version] the installed git version
|
|
418
|
+
#
|
|
419
|
+
# @raise [Git::UnexpectedResultError] if the version string cannot be parsed
|
|
420
|
+
#
|
|
421
|
+
def git_version(timeout: nil)
|
|
422
|
+
@git_version ||= begin
|
|
423
|
+
call_opts = timeout.nil? ? {} : { timeout: timeout }
|
|
424
|
+
Git::Version.parse(Git::Commands::Version.new(self).call(**call_opts).stdout)
|
|
425
|
+
end
|
|
426
|
+
end
|
|
427
|
+
|
|
428
|
+
# Returns a Hash of environment variable overrides for this context
|
|
429
|
+
#
|
|
430
|
+
# Builds the standard git environment from the public accessor methods
|
|
431
|
+
# ({#git_dir}, {#git_work_dir}, {#git_index_file}, {#git_ssh}), then
|
|
432
|
+
# merges any per-call `additional_overrides` on top.
|
|
433
|
+
#
|
|
434
|
+
# Per `Process.spawn` semantics, a value of `nil` unsets the variable.
|
|
435
|
+
#
|
|
436
|
+
# @param additional_overrides [Hash{String => String, nil}] per-call
|
|
437
|
+
# environment overrides keyed by variable name
|
|
438
|
+
#
|
|
439
|
+
# Pass string environment variable names. Ruby preserves string keys when
|
|
440
|
+
# callers forward a string-keyed Hash with `**`.
|
|
441
|
+
#
|
|
442
|
+
# @option additional_overrides [String, nil] :"ENV_VAR" value for an arbitrary
|
|
443
|
+
# environment variable name
|
|
444
|
+
#
|
|
445
|
+
# @return [Hash{String => String, nil}] the merged environment variable overrides
|
|
446
|
+
#
|
|
447
|
+
# @api private
|
|
448
|
+
#
|
|
449
|
+
def env_overrides(**additional_overrides)
|
|
450
|
+
{
|
|
451
|
+
'GIT_DIR' => git_dir,
|
|
452
|
+
'GIT_WORK_TREE' => git_work_dir,
|
|
453
|
+
'GIT_INDEX_FILE' => git_index_file,
|
|
454
|
+
'GIT_SSH' => git_ssh,
|
|
455
|
+
'GIT_EDITOR' => 'true',
|
|
456
|
+
# Pin the locale so git's behavior does not depend on the user's environment.
|
|
457
|
+
# Added for issue #753, where a German user's `git branch` output
|
|
458
|
+
# ("* (HEAD losgelöst bei origin/25.1)") broke branch parsing.
|
|
459
|
+
#
|
|
460
|
+
# The pin has two halves, and the load-bearing one is not the obvious one:
|
|
461
|
+
#
|
|
462
|
+
# - Messages: keeps git's human-readable text in English. This is now only
|
|
463
|
+
# defense-in-depth — lib/ reads `--format=%(refname:short)` rather than
|
|
464
|
+
# git's prose, and matches no English git message.
|
|
465
|
+
# - Ctype: makes git's regex engine match *characters* rather than *bytes*.
|
|
466
|
+
# This is the half that matters. Under a C ctype, `grep`, `log`, and the
|
|
467
|
+
# `config` value regexes silently return wrong answers — with exit status
|
|
468
|
+
# zero — for any pattern whose metacharacters span non-ASCII text.
|
|
469
|
+
#
|
|
470
|
+
# So do not "simplify" this to `C`: that yields English messages and a broken
|
|
471
|
+
# ctype, which is the worst of both. A pinned locale the host does not have
|
|
472
|
+
# degrades to that same C ctype, which is why each branch below has to name a
|
|
473
|
+
# locale that actually exists on the platform it applies to:
|
|
474
|
+
#
|
|
475
|
+
# - Non-Darwin: `C.UTF-8`. Stock Debian, Ubuntu, and RHEL images generate no
|
|
476
|
+
# `en_US.UTF-8`, so pinning it there produced exactly the silent breakage
|
|
477
|
+
# above. (musl ignores the locale name for ctype, so Alpine is UTF-8 either
|
|
478
|
+
# way.)
|
|
479
|
+
# - Darwin: `en_US.UTF-8`, which every macOS release ships. `C.UTF-8` did not
|
|
480
|
+
# arrive until macOS 15, so macOS 11–14 — including Intel Macs that are
|
|
481
|
+
# hardware-capped below 15 — would break under it. Delete this branch once
|
|
482
|
+
# macOS 14 and earlier are out of support.
|
|
483
|
+
#
|
|
484
|
+
# Windows takes the non-Darwin branch, where the value makes no difference:
|
|
485
|
+
# Git for Windows folds case and runs PCRE in UTF mode under every value, and
|
|
486
|
+
# matches bytes for `.` and POSIX classes under every value — measured on git
|
|
487
|
+
# 2.55.0 against `en_US.UTF-8`, `C.UTF-8`, `C`, and no pin at all.
|
|
488
|
+
#
|
|
489
|
+
# RHEL 7 (glibc 2.17) has neither locale and gets a C ctype whatever is pinned.
|
|
490
|
+
# It is EOL, and there is deliberately no public override for this value.
|
|
491
|
+
'LC_ALL' => darwin_platform? ? 'en_US.UTF-8' : 'C.UTF-8'
|
|
492
|
+
}.merge(additional_overrides)
|
|
493
|
+
end
|
|
494
|
+
|
|
495
|
+
private
|
|
496
|
+
|
|
497
|
+
# Whether this process is running on macOS
|
|
498
|
+
#
|
|
499
|
+
# Checks `RUBY_DESCRIPTION` as well as `RUBY_PLATFORM` because JRuby reports
|
|
500
|
+
# `RUBY_PLATFORM` as `"java"` on every host and records the real platform only in
|
|
501
|
+
# `RUBY_DESCRIPTION` (as, for example, `"... [arm64-darwin]"`). Testing
|
|
502
|
+
# `RUBY_PLATFORM` alone would put JRuby on macOS onto the non-Darwin branch of the
|
|
503
|
+
# `LC_ALL` pin, which is the one platform pairing that branch must never be given —
|
|
504
|
+
# see {#env_overrides}.
|
|
505
|
+
#
|
|
506
|
+
# Test the platform plainly rather than sniffing the Darwin version: `RUBY_PLATFORM`
|
|
507
|
+
# records the version Ruby was *built* against, so a Ruby built on macOS 14 still
|
|
508
|
+
# reports `darwin23` when run on macOS 15.
|
|
509
|
+
#
|
|
510
|
+
# @return [Boolean] true if this process is running on macOS
|
|
511
|
+
#
|
|
512
|
+
# @api private
|
|
513
|
+
#
|
|
514
|
+
def darwin_platform?
|
|
515
|
+
RUBY_PLATFORM.include?('darwin') || RUBY_DESCRIPTION.include?('darwin')
|
|
516
|
+
end
|
|
517
|
+
|
|
518
|
+
# Returns the Array of git global option strings for this context
|
|
519
|
+
#
|
|
520
|
+
# Prepends `--git-dir` and `--work-tree` when the corresponding attributes
|
|
521
|
+
# are set, then appends {STATIC_GLOBAL_OPTS}.
|
|
522
|
+
#
|
|
523
|
+
# @return [Array<String>] the global options to prepend to every git invocation
|
|
524
|
+
#
|
|
525
|
+
def global_opts
|
|
526
|
+
[].tap do |opts|
|
|
527
|
+
opts << "--git-dir=#{git_dir}" unless git_dir.nil?
|
|
528
|
+
opts << "--work-tree=#{git_work_dir}" unless git_work_dir.nil?
|
|
529
|
+
opts.concat(STATIC_GLOBAL_OPTS)
|
|
530
|
+
end
|
|
531
|
+
end
|
|
532
|
+
|
|
533
|
+
# Creates a {Git::CommandLine::Capturing} instance for the current invocation.
|
|
534
|
+
#
|
|
535
|
+
# A new instance is created per call so that {#binary_path} — resolved from
|
|
536
|
+
# `Git::Config.instance` when set to `:use_global_config` — and {#env_overrides}
|
|
537
|
+
# — including {#git_ssh} resolution for `:use_global_config` — reflect the
|
|
538
|
+
# state of {Git::Config.instance} at the time of each command invocation.
|
|
539
|
+
#
|
|
540
|
+
# @return [Git::CommandLine::Capturing] the capturing command line instance
|
|
541
|
+
#
|
|
542
|
+
def command_line_capturing
|
|
543
|
+
Git::CommandLine::Capturing.new(env_overrides, binary_path, global_opts, @logger)
|
|
544
|
+
end
|
|
545
|
+
|
|
546
|
+
# Creates a {Git::CommandLine::Streaming} instance for the current invocation.
|
|
547
|
+
#
|
|
548
|
+
# A new instance is created per call so that {#binary_path} — resolved from
|
|
549
|
+
# `Git::Config.instance` when set to `:use_global_config` — and {#env_overrides}
|
|
550
|
+
# — including {#git_ssh} resolution for `:use_global_config` — reflect the
|
|
551
|
+
# state of {Git::Config.instance} at the time of each command invocation.
|
|
552
|
+
#
|
|
553
|
+
# @return [Git::CommandLine::Streaming] the streaming command line instance
|
|
554
|
+
#
|
|
555
|
+
def command_line_streaming
|
|
556
|
+
Git::CommandLine::Streaming.new(env_overrides, binary_path, global_opts, @logger)
|
|
557
|
+
end
|
|
558
|
+
end
|
|
559
|
+
end
|