git 4.3.2 → 5.0.0.beta.2
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/.github/copilot-instructions.md +73 -2705
- data/.github/prompts/iteratively-address-copilot-reviews.prompt.md +188 -0
- data/.github/pull_request_template.md +3 -1
- data/.github/skills/breaking-change-analysis/SKILL.md +102 -0
- data/.github/skills/ci-cd-troubleshooting/SKILL.md +264 -0
- data/.github/skills/command-implementation/REFERENCE.md +993 -0
- data/.github/skills/command-implementation/SKILL.md +229 -0
- data/.github/skills/command-test-conventions/SKILL.md +660 -0
- data/.github/skills/command-yard-documentation/SKILL.md +426 -0
- data/.github/skills/dependency-management/SKILL.md +72 -0
- data/.github/skills/development-workflow/SKILL.md +506 -0
- data/.github/skills/extract-command-from-lib/SKILL.md +487 -0
- data/.github/skills/extract-facade-from-base-lib/KEYWORD_ARG_REMEDIATION.md +22 -0
- data/.github/skills/extract-facade-from-base-lib/SKILL.md +600 -0
- data/.github/skills/facade-implementation/REFERENCE.md +840 -0
- data/.github/skills/facade-implementation/SKILL.md +274 -0
- data/.github/skills/facade-test-conventions/SKILL.md +394 -0
- data/.github/skills/facade-yard-documentation/SKILL.md +429 -0
- data/.github/skills/make-skill-template/SKILL.md +176 -0
- data/.github/skills/pr-readiness-review/SKILL.md +185 -0
- data/.github/skills/project-context/SKILL.md +313 -0
- data/.github/skills/pull-request-review/SKILL.md +168 -0
- data/.github/skills/refactor-command-to-commandlineresult/SKILL.md +131 -0
- data/.github/skills/release-management/SKILL.md +125 -0
- data/.github/skills/review-arguments-dsl/CHECKLIST.md +788 -0
- data/.github/skills/review-arguments-dsl/SKILL.md +214 -0
- data/.github/skills/review-backward-compatibility/SKILL.md +275 -0
- data/.github/skills/review-cross-command-consistency/SKILL.md +139 -0
- data/.github/skills/reviewing-skills/SKILL.md +189 -0
- data/.github/skills/rspec-unit-testing-standards/SKILL.md +639 -0
- data/.github/skills/tdd-refactor-step/SKILL.md +236 -0
- data/.github/skills/test-debugging/SKILL.md +160 -0
- data/.github/skills/yard-documentation/SKILL.md +793 -0
- data/.github/workflows/continuous_integration.yml +3 -2
- data/.github/workflows/enforce_conventional_commits.yml +1 -1
- data/.github/workflows/experimental_continuous_integration.yml +2 -2
- data/.github/workflows/release.yml +3 -4
- data/.gitignore +8 -0
- data/.husky/pre-commit +13 -0
- data/.release-please-manifest.json +1 -1
- data/.rspec +3 -0
- data/.rubocop.yml +12 -3
- data/.rubocop_todo.yml +23 -5
- data/.yardopts +1 -0
- data/CHANGELOG.md +0 -40
- data/CONTRIBUTING.md +694 -53
- data/README.md +67 -15
- data/Rakefile +61 -9
- data/UPGRADING.md +141 -0
- data/commitlint.test +4 -0
- data/git.gemspec +18 -7
- data/lib/git/args_builder.rb +0 -8
- data/lib/git/branch.rb +369 -43
- data/lib/git/branch_delete_failure.rb +31 -0
- data/lib/git/branch_delete_result.rb +63 -0
- data/lib/git/branch_info.rb +178 -0
- data/lib/git/branches.rb +122 -24
- data/lib/git/command_line/base.rb +255 -0
- data/lib/git/command_line/capturing.rb +251 -0
- data/lib/git/command_line/result.rb +96 -0
- data/lib/git/command_line/streaming.rb +196 -0
- data/lib/git/command_line.rb +43 -322
- data/lib/git/command_line_result.rb +4 -88
- data/lib/git/commands/add.rb +131 -0
- data/lib/git/commands/am/abort.rb +43 -0
- data/lib/git/commands/am/apply.rb +252 -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 +47 -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 +237 -0
- data/lib/git/commands/archive/list_formats.rb +46 -0
- data/lib/git/commands/archive.rb +140 -0
- data/lib/git/commands/arguments.rb +3510 -0
- data/lib/git/commands/base.rb +404 -0
- data/lib/git/commands/branch/copy.rb +94 -0
- data/lib/git/commands/branch/create.rb +173 -0
- data/lib/git/commands/branch/delete.rb +80 -0
- data/lib/git/commands/branch/list.rb +162 -0
- data/lib/git/commands/branch/move.rb +94 -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 +57 -0
- data/lib/git/commands/branch.rb +34 -0
- data/lib/git/commands/cat_file/batch.rb +369 -0
- data/lib/git/commands/cat_file/filtered.rb +105 -0
- data/lib/git/commands/cat_file/raw.rb +216 -0
- data/lib/git/commands/cat_file.rb +49 -0
- data/lib/git/commands/checkout/branch.rb +151 -0
- data/lib/git/commands/checkout/files.rb +115 -0
- data/lib/git/commands/checkout.rb +38 -0
- data/lib/git/commands/checkout_index.rb +105 -0
- data/lib/git/commands/clean.rb +100 -0
- data/lib/git/commands/clone.rb +240 -0
- data/lib/git/commands/commit.rb +272 -0
- data/lib/git/commands/commit_tree.rb +100 -0
- data/lib/git/commands/config_option_syntax/add.rb +83 -0
- data/lib/git/commands/config_option_syntax/get.rb +117 -0
- data/lib/git/commands/config_option_syntax/get_all.rb +115 -0
- data/lib/git/commands/config_option_syntax/get_color.rb +91 -0
- data/lib/git/commands/config_option_syntax/get_color_bool.rb +93 -0
- data/lib/git/commands/config_option_syntax/get_regexp.rb +115 -0
- data/lib/git/commands/config_option_syntax/get_urlmatch.rb +107 -0
- data/lib/git/commands/config_option_syntax/list.rb +107 -0
- data/lib/git/commands/config_option_syntax/remove_section.rb +74 -0
- data/lib/git/commands/config_option_syntax/rename_section.rb +78 -0
- data/lib/git/commands/config_option_syntax/replace_all.rb +104 -0
- data/lib/git/commands/config_option_syntax/set.rb +114 -0
- data/lib/git/commands/config_option_syntax/unset.rb +89 -0
- data/lib/git/commands/config_option_syntax/unset_all.rb +89 -0
- data/lib/git/commands/config_option_syntax.rb +56 -0
- data/lib/git/commands/describe.rb +155 -0
- data/lib/git/commands/diff.rb +656 -0
- data/lib/git/commands/diff_files.rb +518 -0
- data/lib/git/commands/diff_index.rb +496 -0
- data/lib/git/commands/fetch.rb +352 -0
- data/lib/git/commands/fsck.rb +136 -0
- data/lib/git/commands/gc.rb +132 -0
- data/lib/git/commands/grep.rb +338 -0
- data/lib/git/commands/init.rb +99 -0
- data/lib/git/commands/log.rb +632 -0
- data/lib/git/commands/ls_files.rb +191 -0
- data/lib/git/commands/ls_remote.rb +155 -0
- data/lib/git/commands/ls_tree.rb +131 -0
- data/lib/git/commands/maintenance/register.rb +75 -0
- data/lib/git/commands/maintenance/run.rb +104 -0
- data/lib/git/commands/maintenance/start.rb +66 -0
- data/lib/git/commands/maintenance/stop.rb +55 -0
- data/lib/git/commands/maintenance/unregister.rb +79 -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 +245 -0
- data/lib/git/commands/merge.rb +28 -0
- data/lib/git/commands/merge_base.rb +86 -0
- data/lib/git/commands/mv.rb +77 -0
- data/lib/git/commands/name_rev.rb +114 -0
- data/lib/git/commands/pull.rb +377 -0
- data/lib/git/commands/push.rb +246 -0
- data/lib/git/commands/read_tree.rb +149 -0
- data/lib/git/commands/remote/add.rb +91 -0
- data/lib/git/commands/remote/get_url.rb +66 -0
- data/lib/git/commands/remote/list.rb +54 -0
- data/lib/git/commands/remote/prune.rb +61 -0
- data/lib/git/commands/remote/remove.rb +52 -0
- data/lib/git/commands/remote/rename.rb +69 -0
- data/lib/git/commands/remote/set_branches.rb +63 -0
- data/lib/git/commands/remote/set_head.rb +82 -0
- data/lib/git/commands/remote/set_url.rb +71 -0
- data/lib/git/commands/remote/set_url_add.rb +61 -0
- data/lib/git/commands/remote/set_url_delete.rb +64 -0
- data/lib/git/commands/remote/show.rb +71 -0
- data/lib/git/commands/remote/update.rb +72 -0
- data/lib/git/commands/remote.rb +42 -0
- data/lib/git/commands/repack.rb +277 -0
- data/lib/git/commands/reset.rb +147 -0
- data/lib/git/commands/rev_parse.rb +297 -0
- data/lib/git/commands/revert/abort.rb +45 -0
- data/lib/git/commands/revert/continue.rb +57 -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 +153 -0
- data/lib/git/commands/revert.rb +29 -0
- data/lib/git/commands/rm.rb +114 -0
- data/lib/git/commands/show.rb +632 -0
- data/lib/git/commands/show_ref/exclude_existing.rb +120 -0
- data/lib/git/commands/show_ref/exists.rb +78 -0
- data/lib/git/commands/show_ref/list.rb +145 -0
- data/lib/git/commands/show_ref/verify.rb +120 -0
- data/lib/git/commands/show_ref.rb +42 -0
- data/lib/git/commands/stash/apply.rb +75 -0
- data/lib/git/commands/stash/branch.rb +65 -0
- data/lib/git/commands/stash/clear.rb +41 -0
- data/lib/git/commands/stash/create.rb +58 -0
- data/lib/git/commands/stash/drop.rb +67 -0
- data/lib/git/commands/stash/list.rb +39 -0
- data/lib/git/commands/stash/pop.rb +78 -0
- data/lib/git/commands/stash/push.rb +103 -0
- data/lib/git/commands/stash/show.rb +149 -0
- data/lib/git/commands/stash/store.rb +63 -0
- data/lib/git/commands/stash.rb +38 -0
- data/lib/git/commands/status.rb +169 -0
- data/lib/git/commands/symbolic_ref/delete.rb +68 -0
- data/lib/git/commands/symbolic_ref/read.rb +95 -0
- data/lib/git/commands/symbolic_ref/update.rb +76 -0
- data/lib/git/commands/symbolic_ref.rb +38 -0
- data/lib/git/commands/tag/create.rb +139 -0
- data/lib/git/commands/tag/delete.rb +55 -0
- data/lib/git/commands/tag/list.rb +143 -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 +140 -0
- data/lib/git/commands/update_ref/delete.rb +92 -0
- data/lib/git/commands/update_ref/update.rb +106 -0
- data/lib/git/commands/update_ref.rb +42 -0
- data/lib/git/commands/version.rb +57 -0
- data/lib/git/commands/worktree/add.rb +140 -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 +87 -0
- data/lib/git/config.rb +17 -0
- data/lib/git/config_entry_info.rb +106 -0
- data/lib/git/configuring.rb +665 -0
- data/lib/git/deprecation.rb +9 -0
- data/lib/git/detached_head_info.rb +54 -0
- data/lib/git/diff.rb +293 -7
- data/lib/git/diff_file_numstat_info.rb +29 -0
- data/lib/git/diff_file_patch_info.rb +134 -0
- data/lib/git/diff_file_raw_info.rb +127 -0
- data/lib/git/diff_info.rb +169 -0
- data/lib/git/diff_path_status.rb +67 -19
- data/lib/git/diff_result.rb +32 -0
- data/lib/git/diff_stats.rb +51 -14
- data/lib/git/dirstat_info.rb +86 -0
- data/lib/git/errors.rb +65 -2
- data/lib/git/execution_context/global.rb +31 -0
- data/lib/git/execution_context/repository.rb +136 -0
- data/lib/git/execution_context.rb +501 -0
- data/lib/git/file_ref.rb +74 -0
- data/lib/git/fsck_object.rb +9 -9
- data/lib/git/fsck_result.rb +1 -1
- data/lib/git/log.rb +9 -2
- data/lib/git/object.rb +90 -27
- data/lib/git/parsers/branch.rb +224 -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 +585 -0
- data/lib/git/parsers/fsck.rb +133 -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/stash.rb +208 -0
- data/lib/git/parsers/tag.rb +257 -0
- data/lib/git/remote.rb +120 -9
- data/lib/git/repository/branching.rb +743 -0
- data/lib/git/repository/committing.rb +187 -0
- data/lib/git/repository/configuring.rb +351 -0
- data/lib/git/repository/context_helpers.rb +264 -0
- data/lib/git/repository/diffing.rb +775 -0
- data/lib/git/repository/factories.rb +682 -0
- data/lib/git/repository/inspecting.rb +252 -0
- data/lib/git/repository/logging.rb +247 -0
- data/lib/git/repository/maintenance.rb +65 -0
- data/lib/git/repository/merging.rb +357 -0
- data/lib/git/repository/object_operations.rb +1199 -0
- data/lib/git/repository/path_resolver.rb +207 -0
- data/lib/git/repository/remote_operations.rb +898 -0
- data/lib/git/repository/shared_private.rb +51 -0
- data/lib/git/repository/staging.rb +554 -0
- data/lib/git/repository/stashing.rb +143 -0
- data/lib/git/repository/status_operations.rb +201 -0
- data/lib/git/repository/worktree_operations.rb +159 -0
- data/lib/git/repository.rb +203 -1
- data/lib/git/stash.rb +78 -4
- data/lib/git/stash_info.rb +104 -0
- data/lib/git/stashes.rb +125 -13
- data/lib/git/status.rb +215 -18
- data/lib/git/tag_delete_failure.rb +31 -0
- data/lib/git/tag_delete_result.rb +63 -0
- data/lib/git/tag_info.rb +105 -0
- data/lib/git/version.rb +109 -2
- data/lib/git/version_constraint.rb +81 -0
- data/lib/git/worktree.rb +108 -6
- data/lib/git/worktrees.rb +94 -7
- data/lib/git.rb +260 -61
- data/redesign/1_architecture_existing.md +54 -18
- data/redesign/2_architecture_redesign.md +365 -46
- data/redesign/3_architecture_implementation.md +1497 -63
- data/redesign/Phase 4 - Step A.md +360 -0
- data/redesign/beta_release.md +107 -0
- data/redesign/c1c2_audit.md +566 -0
- data/redesign/c1c2_bucket6_lib_orphans.md +626 -0
- data/redesign/config_design.rb +501 -0
- data/tasks/gem_tasks.rake +4 -0
- data/tasks/npm_tasks.rake +7 -0
- data/tasks/rspec.rake +48 -0
- data/tasks/test.rake +13 -1
- data/tasks/yard.rake +34 -7
- metadata +365 -22
- data/lib/git/base.rb +0 -1128
- data/lib/git/index.rb +0 -6
- data/lib/git/lib.rb +0 -2277
- data/lib/git/path.rb +0 -38
- data/lib/git/working_directory.rb +0 -6
- /data/{release-please-config.json → .release-please-config.json} +0 -0
data/lib/git.rb
CHANGED
|
@@ -3,36 +3,64 @@
|
|
|
3
3
|
require 'active_support'
|
|
4
4
|
require 'active_support/deprecation'
|
|
5
5
|
|
|
6
|
+
require 'git/deprecation'
|
|
7
|
+
require 'git/version'
|
|
8
|
+
|
|
6
9
|
module Git
|
|
7
|
-
|
|
10
|
+
# Minimum git version required by this gem
|
|
11
|
+
#
|
|
12
|
+
# Commands and features may require newer versions, but this is the absolute
|
|
13
|
+
# minimum supported version for the gem as a whole.
|
|
14
|
+
#
|
|
15
|
+
# @return [Git::Version]
|
|
16
|
+
#
|
|
17
|
+
# @api public
|
|
18
|
+
#
|
|
19
|
+
MINIMUM_GIT_VERSION = Version.parse('2.28.0')
|
|
8
20
|
end
|
|
9
21
|
|
|
10
22
|
require 'git/author'
|
|
11
|
-
require 'git/base'
|
|
12
23
|
require 'git/branch'
|
|
24
|
+
require 'git/branch_info'
|
|
13
25
|
require 'git/branches'
|
|
14
26
|
require 'git/command_line_result'
|
|
15
27
|
require 'git/command_line'
|
|
28
|
+
require 'process_executer'
|
|
16
29
|
require 'git/config'
|
|
30
|
+
require 'git/config_entry_info'
|
|
31
|
+
require 'git/parsers/config_entry'
|
|
32
|
+
require 'git/configuring'
|
|
17
33
|
require 'git/diff'
|
|
34
|
+
require 'git/diff_file_numstat_info'
|
|
35
|
+
require 'git/diff_file_patch_info'
|
|
36
|
+
require 'git/diff_file_raw_info'
|
|
37
|
+
require 'git/diff_info'
|
|
38
|
+
require 'git/parsers/diff'
|
|
39
|
+
require 'git/diff_result'
|
|
40
|
+
require 'git/dirstat_info'
|
|
18
41
|
require 'git/encoding_utils'
|
|
19
42
|
require 'git/errors'
|
|
20
43
|
require 'git/escaped_path'
|
|
44
|
+
require 'git/execution_context'
|
|
45
|
+
require 'git/execution_context/global'
|
|
46
|
+
require 'git/file_ref'
|
|
21
47
|
require 'git/fsck_object'
|
|
22
48
|
require 'git/fsck_result'
|
|
23
|
-
require 'git/
|
|
24
|
-
require 'git/
|
|
49
|
+
require 'git/parsers/ls_remote'
|
|
50
|
+
require 'git/version_constraint'
|
|
51
|
+
require 'git/commands'
|
|
25
52
|
require 'git/log'
|
|
26
53
|
require 'git/object'
|
|
27
|
-
require 'git/path'
|
|
28
54
|
require 'git/remote'
|
|
29
55
|
require 'git/repository'
|
|
30
56
|
require 'git/status'
|
|
31
57
|
require 'git/stash'
|
|
58
|
+
require 'git/stash_info'
|
|
32
59
|
require 'git/stashes'
|
|
60
|
+
require 'git/tag_delete_failure'
|
|
61
|
+
require 'git/tag_delete_result'
|
|
62
|
+
require 'git/tag_info'
|
|
33
63
|
require 'git/url'
|
|
34
|
-
require 'git/version'
|
|
35
|
-
require 'git/working_directory'
|
|
36
64
|
require 'git/worktree'
|
|
37
65
|
require 'git/worktrees'
|
|
38
66
|
|
|
@@ -43,35 +71,56 @@ require 'git/worktrees'
|
|
|
43
71
|
#
|
|
44
72
|
# @author Scott Chacon (mailto:schacon@gmail.com)
|
|
45
73
|
#
|
|
46
|
-
module Git
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
#
|
|
50
|
-
#
|
|
74
|
+
module Git
|
|
75
|
+
extend Git::Configuring
|
|
76
|
+
|
|
77
|
+
# @deprecated Mixing in the `Git` module is deprecated and will be removed in v6.0.0.
|
|
78
|
+
# Use `Git.open(Dir.pwd).config(...)` instead.
|
|
51
79
|
def config(name = nil, value = nil)
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
# return value
|
|
58
|
-
lib.config_get(name)
|
|
59
|
-
else
|
|
60
|
-
# return hash
|
|
61
|
-
lib.config_list
|
|
62
|
-
end
|
|
80
|
+
Git::Deprecation.warn(
|
|
81
|
+
'Git#config is deprecated and will be removed in v6.0.0. ' \
|
|
82
|
+
'Use Git.open(Dir.pwd).config(...) instead.'
|
|
83
|
+
)
|
|
84
|
+
Git.__send__(:run_config_utility, name, value, global: false)
|
|
63
85
|
end
|
|
64
86
|
|
|
87
|
+
# Configures the gem by yielding {Git::Config.instance} to the block
|
|
88
|
+
#
|
|
89
|
+
# @example Set the global git binary path
|
|
90
|
+
# Git.configure { |c| c.binary_path = '/usr/local/bin/git' }
|
|
91
|
+
#
|
|
92
|
+
# @yield [config] yields the singleton config object
|
|
93
|
+
#
|
|
94
|
+
# @yieldparam config [Git::Config] the singleton config object
|
|
95
|
+
#
|
|
96
|
+
# @yieldreturn [void]
|
|
97
|
+
#
|
|
98
|
+
# @return [void]
|
|
99
|
+
#
|
|
65
100
|
def self.configure
|
|
66
|
-
yield
|
|
101
|
+
yield Git::Config.instance
|
|
102
|
+
nil
|
|
67
103
|
end
|
|
68
104
|
|
|
105
|
+
# Returns the process-wide {Git::Config} singleton
|
|
106
|
+
#
|
|
107
|
+
# @example Read the configured binary path
|
|
108
|
+
# Git.config.binary_path #=> "git"
|
|
109
|
+
#
|
|
110
|
+
# @return [Git::Config] the singleton config object
|
|
111
|
+
#
|
|
69
112
|
def self.config
|
|
70
|
-
|
|
113
|
+
Git::Config.instance
|
|
71
114
|
end
|
|
72
115
|
|
|
116
|
+
# @deprecated Mixing in the `Git` module is deprecated and will be removed in v6.0.0.
|
|
117
|
+
# Use `Git.global_config(...)` instead.
|
|
73
118
|
def global_config(name = nil, value = nil)
|
|
74
|
-
|
|
119
|
+
Git::Deprecation.warn(
|
|
120
|
+
'Git#global_config is deprecated and will be removed in v6.0.0. ' \
|
|
121
|
+
'Use Git.global_config(...) instead.'
|
|
122
|
+
)
|
|
123
|
+
Git.global_config(name, value)
|
|
75
124
|
end
|
|
76
125
|
|
|
77
126
|
# Open a bare repository
|
|
@@ -105,11 +154,11 @@ module Git # rubocop:disable Style/OneClassPerFile
|
|
|
105
154
|
# are logged at the `:info` level. Additional logging is done at the `:debug`
|
|
106
155
|
# level.
|
|
107
156
|
#
|
|
108
|
-
# @return [Git::
|
|
157
|
+
# @return [Git::Repository] an object that can execute git commands in the context
|
|
109
158
|
# of the bare repository.
|
|
110
159
|
#
|
|
111
160
|
def self.bare(git_dir, options = {})
|
|
112
|
-
|
|
161
|
+
Git::Repository.bare(git_dir, options)
|
|
113
162
|
end
|
|
114
163
|
|
|
115
164
|
# Clone a repository into an empty or newly created directory
|
|
@@ -123,9 +172,9 @@ module Git # rubocop:disable Style/OneClassPerFile
|
|
|
123
172
|
#
|
|
124
173
|
# @param directory [Pathname, nil] The directory to clone into
|
|
125
174
|
#
|
|
126
|
-
# If `directory` is a relative
|
|
127
|
-
# given. If `
|
|
128
|
-
# directory.
|
|
175
|
+
# If `directory` is a relative path it is relative to the `:chdir` option if
|
|
176
|
+
# given. If `:chdir` is not given, `directory` is relative to the current
|
|
177
|
+
# working directory.
|
|
129
178
|
#
|
|
130
179
|
# If `nil`, `directory` will be set to the basename of the last component of
|
|
131
180
|
# the path from the `repository_url`. For example, for the URL:
|
|
@@ -176,9 +225,14 @@ module Git # rubocop:disable Style/OneClassPerFile
|
|
|
176
225
|
# @option options [String] :origin Use the value instead `origin` to track
|
|
177
226
|
# the upstream repository.
|
|
178
227
|
#
|
|
179
|
-
# @option options [Pathname] :
|
|
180
|
-
#
|
|
181
|
-
# `
|
|
228
|
+
# @option options [Pathname] :chdir Run `git clone` from within this directory.
|
|
229
|
+
#
|
|
230
|
+
# The `directory` parameter (or the repository basename when `directory` is nil)
|
|
231
|
+
# is resolved relative to `:chdir`, just as if you had `cd`'d into it before
|
|
232
|
+
# running `git clone`. The returned path is the join of `:chdir` and the
|
|
233
|
+
# cloned directory path.
|
|
234
|
+
#
|
|
235
|
+
# @option options [Pathname] :path Deprecated — use `:chdir` instead.
|
|
182
236
|
#
|
|
183
237
|
# @option options [Boolean] :recursive After the clone is created, initialize
|
|
184
238
|
# all submodules within, using their default settings.
|
|
@@ -191,8 +245,10 @@ module Git # rubocop:disable Style/OneClassPerFile
|
|
|
191
245
|
#
|
|
192
246
|
# @example Clone into a different directory `my-ruby-git`
|
|
193
247
|
# git = Git.clone('https://github.com/ruby-git/ruby-git.git', 'my-ruby-git')
|
|
194
|
-
#
|
|
195
|
-
#
|
|
248
|
+
#
|
|
249
|
+
# @example Clone into a specific parent directory
|
|
250
|
+
# git = Git.clone('https://github.com/ruby-git/ruby-git.git', chdir: '/path/to/projects')
|
|
251
|
+
# # clones into /path/to/projects/ruby-git
|
|
196
252
|
#
|
|
197
253
|
# @example Create a bare repository in the directory `ruby-git.git`
|
|
198
254
|
# git = Git.clone('https://github.com/ruby-git/ruby-git.git', bare: true)
|
|
@@ -216,13 +272,11 @@ module Git # rubocop:disable Style/OneClassPerFile
|
|
|
216
272
|
# git_ssh: 'ssh -i /path/to/private_key'
|
|
217
273
|
# )
|
|
218
274
|
#
|
|
219
|
-
# @return [Git::
|
|
275
|
+
# @return [Git::Repository] an object that can execute git commands in the context
|
|
220
276
|
# of the cloned local working copy or cloned repository.
|
|
221
277
|
#
|
|
222
278
|
def self.clone(repository_url, directory = nil, options = {})
|
|
223
|
-
|
|
224
|
-
directory ||= Git::URL.clone_to(repository_url, **clone_to_options)
|
|
225
|
-
Base.clone(repository_url, directory, options)
|
|
279
|
+
Git::Repository.clone(repository_url, directory, options)
|
|
226
280
|
end
|
|
227
281
|
|
|
228
282
|
# Returns the name of the default branch of the given repository
|
|
@@ -263,7 +317,9 @@ module Git # rubocop:disable Style/OneClassPerFile
|
|
|
263
317
|
# @return [String] the name of the default branch
|
|
264
318
|
#
|
|
265
319
|
def self.default_branch(repository, options = {})
|
|
266
|
-
|
|
320
|
+
context = Git::ExecutionContext::Global.new(logger: options[:log])
|
|
321
|
+
output = Git::Commands::LsRemote.new(context).call(repository, 'HEAD', symref: true).stdout
|
|
322
|
+
Git::Parsers::LsRemote.parse_default_branch(output)
|
|
267
323
|
end
|
|
268
324
|
|
|
269
325
|
# Export the current HEAD (or a branch, if <tt>options[:branch]</tt>
|
|
@@ -287,17 +343,7 @@ module Git # rubocop:disable Style/OneClassPerFile
|
|
|
287
343
|
# g.config('user.name') # returns 'Scott Chacon'
|
|
288
344
|
# g.config # returns whole config hash
|
|
289
345
|
def self.global_config(name = nil, value = nil)
|
|
290
|
-
|
|
291
|
-
if name && value
|
|
292
|
-
# set value
|
|
293
|
-
lib.global_config_set(name, value)
|
|
294
|
-
elsif name
|
|
295
|
-
# return value
|
|
296
|
-
lib.global_config_get(name)
|
|
297
|
-
else
|
|
298
|
-
# return hash
|
|
299
|
-
lib.global_config_list
|
|
300
|
-
end
|
|
346
|
+
run_config_utility(name, value, global: true)
|
|
301
347
|
end
|
|
302
348
|
|
|
303
349
|
# Create an empty Git repository or reinitialize an existing Git repository
|
|
@@ -329,17 +375,23 @@ module Git # rubocop:disable Style/OneClassPerFile
|
|
|
329
375
|
# and converted to an absolute path using
|
|
330
376
|
# [File.expand_path](https://www.rubydoc.info/stdlib/core/File.expand_path).
|
|
331
377
|
#
|
|
378
|
+
# @option options [Pathname] :separate_git_dir Alias for `:repository`.
|
|
379
|
+
#
|
|
332
380
|
# @option options [String, nil] :git_ssh An optional custom SSH command
|
|
333
381
|
#
|
|
334
382
|
# - If not specified, uses the global config (Git.configure { |c| c.git_ssh = ... }).
|
|
335
383
|
# - If nil, disables SSH for this instance.
|
|
336
384
|
# - If a non-empty string, uses that value for this instance.
|
|
337
385
|
#
|
|
386
|
+
# @option options [String, :use_global_config] :binary_path path to the git
|
|
387
|
+
# binary; defaults to `Git::Config.instance.binary_path` when not specified.
|
|
388
|
+
# Raises `ArgumentError` if set to `nil`.
|
|
389
|
+
#
|
|
338
390
|
# @option options [Logger] :log A logger to use for Git operations. Git
|
|
339
391
|
# commands are logged at the `:info` level. Additional logging is done
|
|
340
392
|
# at the `:debug` level.
|
|
341
393
|
#
|
|
342
|
-
# @return [Git::
|
|
394
|
+
# @return [Git::Repository] an object that can execute git commands in the context
|
|
343
395
|
# of the newly initialized repository
|
|
344
396
|
#
|
|
345
397
|
# @example Initialize a repository in the current directory
|
|
@@ -357,9 +409,24 @@ module Git # rubocop:disable Style/OneClassPerFile
|
|
|
357
409
|
# @see https://git-scm.com/docs/git-init git init
|
|
358
410
|
#
|
|
359
411
|
def self.init(directory = '.', options = {})
|
|
360
|
-
|
|
412
|
+
Git::Repository.init(directory, options)
|
|
361
413
|
end
|
|
362
414
|
|
|
415
|
+
# Option keys accepted by {.ls_remote}
|
|
416
|
+
#
|
|
417
|
+
# Parser-incompatible options such as `:get_url` and `:symref` are intentionally
|
|
418
|
+
# excluded because {Git::Parsers::LsRemote.parse_output} cannot handle the
|
|
419
|
+
# non-standard output formats those flags produce.
|
|
420
|
+
#
|
|
421
|
+
# @return [Array<Symbol>]
|
|
422
|
+
#
|
|
423
|
+
# @api private
|
|
424
|
+
#
|
|
425
|
+
LS_REMOTE_ALLOWED_OPTS = %i[
|
|
426
|
+
branches b heads h tags t refs upload_pack quiet q exit_code sort server_option o timeout
|
|
427
|
+
].freeze
|
|
428
|
+
private_constant :LS_REMOTE_ALLOWED_OPTS
|
|
429
|
+
|
|
363
430
|
# returns a Hash containing information about the references
|
|
364
431
|
# of the target repository
|
|
365
432
|
#
|
|
@@ -369,7 +436,15 @@ module Git # rubocop:disable Style/OneClassPerFile
|
|
|
369
436
|
# @param [String|NilClass] location the target repository location or nil for '.'
|
|
370
437
|
# @return [{String=>Hash}] the available references of the target repo.
|
|
371
438
|
def self.ls_remote(location = nil, options = {})
|
|
372
|
-
|
|
439
|
+
options = options.dup
|
|
440
|
+
log = options.delete(:log)
|
|
441
|
+
unknown = options.keys - LS_REMOTE_ALLOWED_OPTS
|
|
442
|
+
raise ArgumentError, "Unknown options: #{unknown.join(', ')}" unless unknown.empty?
|
|
443
|
+
|
|
444
|
+
context = Git::ExecutionContext::Global.new(logger: log)
|
|
445
|
+
repository = location || '.'
|
|
446
|
+
output_lines = Git::Commands::LsRemote.new(context).call(repository, **options).stdout.split("\n")
|
|
447
|
+
Git::Parsers::LsRemote.parse_output(output_lines)
|
|
373
448
|
end
|
|
374
449
|
|
|
375
450
|
# Open a an existing Git working directory
|
|
@@ -419,21 +494,145 @@ module Git # rubocop:disable Style/OneClassPerFile
|
|
|
419
494
|
# commands are logged at the `:info` level. Additional logging is done
|
|
420
495
|
# at the `:debug` level.
|
|
421
496
|
#
|
|
422
|
-
# @return [Git::
|
|
497
|
+
# @return [Git::Repository] an object that can execute git commands in the context
|
|
423
498
|
# of the opened working copy
|
|
424
499
|
#
|
|
425
500
|
def self.open(working_dir, options = {})
|
|
426
|
-
|
|
501
|
+
Git::Repository.open(working_dir, options)
|
|
502
|
+
end
|
|
503
|
+
|
|
504
|
+
# Thread-safe cache for git versions, keyed by binary path.
|
|
505
|
+
@git_version_cache_mutex = Mutex.new
|
|
506
|
+
@git_version_cache = {}
|
|
507
|
+
|
|
508
|
+
# @api private
|
|
509
|
+
def self.cached_git_version(binary_path, &block)
|
|
510
|
+
@git_version_cache_mutex.synchronize do
|
|
511
|
+
@git_version_cache[binary_path] ||= block.call
|
|
512
|
+
end
|
|
513
|
+
end
|
|
514
|
+
|
|
515
|
+
# @api private
|
|
516
|
+
def self.clear_git_version_cache
|
|
517
|
+
@git_version_cache_mutex.synchronize do
|
|
518
|
+
@git_version_cache.clear
|
|
519
|
+
end
|
|
520
|
+
end
|
|
521
|
+
|
|
522
|
+
# Return the version of a git binary as a {Git::Version}
|
|
523
|
+
#
|
|
524
|
+
# @example Default binary
|
|
525
|
+
# Git.git_version #=> #<Git::Version 2.42.0>
|
|
526
|
+
#
|
|
527
|
+
# @example Explicit binary path
|
|
528
|
+
# Git.git_version('/opt/homebrew/bin/git') #=> #<Git::Version 2.42.0>
|
|
529
|
+
#
|
|
530
|
+
# @param binary_path [String, nil] path to the git binary; defaults to
|
|
531
|
+
# `Git::Config.instance.binary_path`
|
|
532
|
+
#
|
|
533
|
+
# @return [Git::Version] the parsed git version
|
|
534
|
+
#
|
|
535
|
+
# @raise [Git::UnexpectedResultError] if the version output cannot be parsed
|
|
536
|
+
#
|
|
537
|
+
# @raise [Git::FailedError] if the git binary exits with a non-zero status
|
|
538
|
+
#
|
|
539
|
+
# @raise [Git::Error] if the binary is not found or fails to launch
|
|
540
|
+
#
|
|
541
|
+
def self.git_version(binary_path = nil)
|
|
542
|
+
path = binary_path || Git::Config.instance.binary_path
|
|
543
|
+
cached_git_version(path) { run_git_version(path) }
|
|
544
|
+
end
|
|
545
|
+
|
|
546
|
+
# @api private
|
|
547
|
+
def self.run_git_version(path)
|
|
548
|
+
output = Git::Commands::Version.new(Git::ExecutionContext::Global.new(binary_path: path)).call.stdout
|
|
549
|
+
Git::Version.parse(output)
|
|
550
|
+
end
|
|
551
|
+
private_class_method :run_git_version
|
|
552
|
+
|
|
553
|
+
# @api private
|
|
554
|
+
def self.run_config_utility(name, value, global:)
|
|
555
|
+
context = Git::ExecutionContext::Global.new
|
|
556
|
+
options = global ? { global: true } : {}
|
|
557
|
+
|
|
558
|
+
return Git::Commands::ConfigOptionSyntax::Set.new(context).call(name, value, **options) if !name.nil? && !value.nil?
|
|
559
|
+
return run_config_get(context, name, options) if name
|
|
560
|
+
|
|
561
|
+
output = Git::Commands::ConfigOptionSyntax::List.new(context).call(**options).stdout
|
|
562
|
+
parse_config_list(output.split("\n"))
|
|
563
|
+
end
|
|
564
|
+
private_class_method :run_config_utility
|
|
565
|
+
|
|
566
|
+
def self.run_config_get(context, name, options)
|
|
567
|
+
result = Git::Commands::ConfigOptionSyntax::Get.new(context).call(name, **options)
|
|
568
|
+
raise Git::FailedError, result if result.status.exitstatus != 0
|
|
569
|
+
|
|
570
|
+
result.stdout
|
|
571
|
+
end
|
|
572
|
+
private_class_method :run_config_get
|
|
573
|
+
|
|
574
|
+
# @api private
|
|
575
|
+
def self.parse_config_list(lines)
|
|
576
|
+
lines.each_with_object({}) do |line, hsh|
|
|
577
|
+
key, value = line.split('=', 2)
|
|
578
|
+
hsh[key] = value || ''
|
|
579
|
+
end
|
|
427
580
|
end
|
|
581
|
+
private_class_method :parse_config_list
|
|
582
|
+
|
|
583
|
+
# @api private
|
|
584
|
+
def self.execution_context
|
|
585
|
+
Git::ExecutionContext::Global.new
|
|
586
|
+
end
|
|
587
|
+
private_class_method :execution_context
|
|
588
|
+
|
|
589
|
+
# Scopes that require an active repository and cannot be used at the Git module level
|
|
590
|
+
#
|
|
591
|
+
# @api private
|
|
592
|
+
#
|
|
593
|
+
REPOSITORY_SPECIFIC_SCOPES = %i[local worktree blob].freeze
|
|
594
|
+
private_constant :REPOSITORY_SPECIFIC_SCOPES
|
|
595
|
+
|
|
596
|
+
# Raises +ArgumentError+ when a repository-specific scope is requested.
|
|
597
|
+
#
|
|
598
|
+
# The +:local+, +:worktree+, and +:blob+ scopes require an active git
|
|
599
|
+
# repository and are therefore not valid at the Git module level.
|
|
600
|
+
#
|
|
601
|
+
# @api private
|
|
602
|
+
#
|
|
603
|
+
def self.assert_valid_scope!(**opts)
|
|
604
|
+
invalid = REPOSITORY_SPECIFIC_SCOPES.select { |s| opts[s] }
|
|
605
|
+
return if invalid.empty?
|
|
606
|
+
|
|
607
|
+
raise ArgumentError, "#{invalid.join(', ')} scope requires a repository"
|
|
608
|
+
end
|
|
609
|
+
private_class_method :assert_valid_scope!
|
|
428
610
|
|
|
429
611
|
# Return the version of the git binary
|
|
430
612
|
#
|
|
431
|
-
# @example
|
|
432
|
-
#
|
|
613
|
+
# @example Basic usage
|
|
614
|
+
# Git.binary_version # => [2, 46, 0]
|
|
615
|
+
#
|
|
616
|
+
# @param binary_path [String, nil] path to the git binary; defaults to
|
|
617
|
+
# `Git::Config.instance.binary_path`
|
|
433
618
|
#
|
|
434
619
|
# @return [Array<Integer>] the version of the git binary
|
|
435
620
|
#
|
|
436
|
-
|
|
437
|
-
|
|
621
|
+
# @deprecated Use {Git.git_version} instead, which returns a
|
|
622
|
+
# {Git::Version} (not an Array)
|
|
623
|
+
#
|
|
624
|
+
# For the legacy array shape, call: `Git.git_version.to_a`.
|
|
625
|
+
# The optional binary_path argument is preserved:
|
|
626
|
+
# `Git.git_version(binary_path)`.
|
|
627
|
+
#
|
|
628
|
+
def self.binary_version(binary_path = nil)
|
|
629
|
+
binary_path ||= Git::Config.instance.binary_path
|
|
630
|
+
Git::Deprecation.warn(
|
|
631
|
+
'Git.binary_version is deprecated and will be removed in 6.0. ' \
|
|
632
|
+
'Use Git.git_version instead, which returns a Git::Version ' \
|
|
633
|
+
'(not an Array). For the legacy array shape, call: Git.git_version.to_a. ' \
|
|
634
|
+
'The optional binary_path argument is preserved: Git.git_version(binary_path).'
|
|
635
|
+
)
|
|
636
|
+
git_version(binary_path).to_a
|
|
438
637
|
end
|
|
439
638
|
end
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
# Analysis of the Current Git Gem Architecture and Its Challenges
|
|
2
2
|
|
|
3
|
-
This document provides an in-depth look at the current architecture of the `git` gem,
|
|
3
|
+
This document provides an in-depth look at the current architecture of the `git` gem,
|
|
4
|
+
outlining its primary components and the design challenges that have emerged over
|
|
5
|
+
time. Understanding these challenges is the key motivation for the proposed
|
|
6
|
+
architectural redesign.
|
|
4
7
|
|
|
5
8
|
- [1. Overview of the Current Architecture](#1-overview-of-the-current-architecture)
|
|
6
9
|
- [2. Key Architectural Challenges](#2-key-architectural-challenges)
|
|
@@ -11,27 +14,43 @@ This document provides an in-depth look at the current architecture of the `git`
|
|
|
11
14
|
|
|
12
15
|
## 1. Overview of the Current Architecture
|
|
13
16
|
|
|
14
|
-
The gem's current design is centered around three main classes: `Git`, `Git::Base`,
|
|
17
|
+
The gem's current design is centered around three main classes: `Git`, `Git::Base`,
|
|
18
|
+
and `Git::Lib`.
|
|
15
19
|
|
|
16
|
-
- **`Git` (Top-Level Module)**: This module serves as the primary public entry point
|
|
20
|
+
- **`Git` (Top-Level Module)**: This module serves as the primary public entry point
|
|
21
|
+
for creating repository objects. It contains class-level factory methods like
|
|
22
|
+
`Git.open`, `Git.clone`, and `Git.init`. It also provides an interface for
|
|
23
|
+
accessing global git configuration settings.
|
|
17
24
|
|
|
18
|
-
**`Git::Base`**: This is the main object that users interact with after creating or
|
|
25
|
+
- **`Git::Base`**: This is the main object that users interact with after creating or
|
|
26
|
+
opening a repository. It holds the high-level public API for most git operations
|
|
27
|
+
(e.g., `g.commit`, `g.add`, `g.status`). It is responsible for managing the
|
|
28
|
+
repository's state, such as the paths to the working directory and the `.git`
|
|
29
|
+
directory.
|
|
19
30
|
|
|
20
|
-
**`Git::Lib`**: This class is intended to be the low-level wrapper around the `git`
|
|
31
|
+
- **`Git::Lib`**: This class is intended to be the low-level wrapper around the `git`
|
|
32
|
+
command-line tool. It contains the methods that build the specific command-line
|
|
33
|
+
arguments and execute the `git` binary. In practice, it also contains a significant
|
|
34
|
+
amount of logic for parsing the output of these commands.
|
|
21
35
|
|
|
22
36
|
## 2. Key Architectural Challenges
|
|
23
37
|
|
|
24
|
-
While this structure has been functional, several significant design challenges make
|
|
38
|
+
While this structure has been functional, several significant design challenges make
|
|
39
|
+
the codebase difficult to maintain, test, and evolve.
|
|
25
40
|
|
|
26
41
|
### A. Unclear Separation of Concerns
|
|
27
42
|
|
|
28
|
-
The responsibilities between Git::Base and Git::Lib are "muddy" and overlap
|
|
43
|
+
The responsibilities between Git::Base and Git::Lib are "muddy" and overlap
|
|
44
|
+
significantly.
|
|
29
45
|
|
|
30
46
|
- `Git::Base` sometimes contains logic that feels like it should be lower-level.
|
|
31
47
|
|
|
32
|
-
- `Git::Lib`, which should ideally only be concerned with command execution, is
|
|
48
|
+
- `Git::Lib`, which should ideally only be concerned with command execution, is
|
|
49
|
+
filled with high-level logic for parsing command output into specific Ruby objects
|
|
50
|
+
(e.g., parsing log output, diff stats, and branch lists).
|
|
33
51
|
|
|
34
|
-
This blending of responsibilities makes it hard to determine where a specific piece
|
|
52
|
+
This blending of responsibilities makes it hard to determine where a specific piece
|
|
53
|
+
of logic should reside, leading to an inconsistent and confusing internal structure.
|
|
35
54
|
|
|
36
55
|
### B. Circular Dependency
|
|
37
56
|
|
|
@@ -39,28 +58,45 @@ This is the most critical architectural flaw in the current design.
|
|
|
39
58
|
|
|
40
59
|
- A `Git::Base` instance is created.
|
|
41
60
|
|
|
42
|
-
- The first time a command is run, `Git::Base` lazily initializes a `Git::Lib`
|
|
61
|
+
- The first time a command is run, `Git::Base` lazily initializes a `Git::Lib`
|
|
62
|
+
instance via its `.lib` accessor method.
|
|
43
63
|
|
|
44
|
-
- The `Git::Lib` constructor is passed the `Git::Base` instance (`self`) so that it
|
|
64
|
+
- The `Git::Lib` constructor is passed the `Git::Base` instance (`self`) so that it
|
|
65
|
+
can read the repository's path configuration back from the object that is creating
|
|
66
|
+
it.
|
|
45
67
|
|
|
46
|
-
This creates a tight, circular coupling: `Git::Base` depends on `Git::Lib` to execute
|
|
68
|
+
This creates a tight, circular coupling: `Git::Base` depends on `Git::Lib` to execute
|
|
69
|
+
commands, but `Git::Lib` depends on `Git::Base` for its own configuration. This
|
|
70
|
+
pattern makes the classes difficult to instantiate or test in isolation and creates a
|
|
71
|
+
fragile system where changes in one class can have unexpected side effects in the
|
|
72
|
+
other.
|
|
47
73
|
|
|
48
74
|
### C. Undefined Public API Boundary
|
|
49
75
|
|
|
50
|
-
The gem lacks a formally defined public interface. Because `Git::Base` exposes its
|
|
76
|
+
The gem lacks a formally defined public interface. Because `Git::Base` exposes its
|
|
77
|
+
internal `Git::Lib` instance via the public `g.lib` accessor, many users have come to
|
|
78
|
+
rely on `Git::Lib` and its methods as if they were part of the public API.
|
|
51
79
|
|
|
52
80
|
This has two negative consequences:
|
|
53
81
|
|
|
54
|
-
1. It prevents the gem's maintainers from refactoring or changing the internal
|
|
82
|
+
1. It prevents the gem's maintainers from refactoring or changing the internal
|
|
83
|
+
implementation of `Git::Lib` without causing breaking changes for users.
|
|
55
84
|
|
|
56
|
-
2. It exposes complex, internal methods to users, creating a confusing and
|
|
85
|
+
2. It exposes complex, internal methods to users, creating a confusing and
|
|
86
|
+
inconsistent user experience.
|
|
57
87
|
|
|
58
88
|
### D. Slow and Brittle Test Suite
|
|
59
89
|
|
|
60
90
|
The current testing strategy, built on `TestUnit`, suffers from two major issues:
|
|
61
91
|
|
|
62
|
-
- **Over-reliance on Fixtures**: Most tests depend on having a complete, physical git
|
|
92
|
+
- **Over-reliance on Fixtures**: Most tests depend on having a complete, physical git
|
|
93
|
+
repository on the filesystem to run against. Managing these fixtures is cumbersome.
|
|
63
94
|
|
|
64
|
-
- **Excessive Shelling Out**: Because the logic for command execution and output
|
|
95
|
+
- **Excessive Shelling Out**: Because the logic for command execution and output
|
|
96
|
+
parsing are tightly coupled, nearly every test must shell out to the actual `git`
|
|
97
|
+
command-line tool.
|
|
65
98
|
|
|
66
|
-
This makes the test suite extremely slow, especially on non-UNIX platforms like
|
|
99
|
+
This makes the test suite extremely slow, especially on non-UNIX platforms like
|
|
100
|
+
Windows where process creation is more expensive. The slow feedback loop discourages
|
|
101
|
+
frequent testing and makes development more difficult. The brittleness of
|
|
102
|
+
filesystem-dependent tests also leads to flickering or unreliable test runs.
|