git 4.3.2 → 5.0.0.beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/copilot-instructions.md +67 -2705
- 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/SKILL.md +586 -0
- data/.github/skills/facade-implementation/REFERENCE.md +840 -0
- data/.github/skills/facade-implementation/SKILL.md +260 -0
- data/.github/skills/facade-test-conventions/SKILL.md +380 -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 +7 -3
- data/.rubocop_todo.yml +23 -5
- data/.yardopts +1 -0
- data/CHANGELOG.md +0 -40
- data/CONTRIBUTING.md +694 -53
- data/README.md +17 -5
- data/Rakefile +61 -9
- data/commitlint.test +4 -0
- data/git.gemspec +14 -8
- data/lib/git/args_builder.rb +0 -8
- data/lib/git/base.rb +486 -410
- data/lib/git/branch.rb +380 -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 +130 -24
- data/lib/git/command_line/base.rb +245 -0
- data/lib/git/command_line/capturing.rb +249 -0
- data/lib/git/command_line/result.rb +96 -0
- data/lib/git/command_line/streaming.rb +194 -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 +403 -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 +364 -0
- data/lib/git/commands/cat_file/filtered.rb +105 -0
- data/lib/git/commands/cat_file/raw.rb +210 -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 +102 -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 +52 -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 +89 -0
- data/lib/git/detached_head_info.rb +54 -0
- data/lib/git/diff.rb +297 -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 +78 -19
- data/lib/git/diff_result.rb +32 -0
- data/lib/git/diff_stats.rb +59 -14
- data/lib/git/dirstat_info.rb +86 -0
- data/lib/git/errors.rb +65 -2
- data/lib/git/execution_context/global.rb +56 -0
- data/lib/git/execution_context/repository.rb +147 -0
- data/lib/git/execution_context.rb +482 -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/lib.rb +1606 -1028
- data/lib/git/log.rb +15 -2
- data/lib/git/object.rb +92 -22
- data/lib/git/parsers/branch.rb +224 -0
- data/lib/git/parsers/cat_file.rb +111 -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_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 +133 -9
- data/lib/git/repository/branching.rb +572 -0
- data/lib/git/repository/committing.rb +191 -0
- data/lib/git/repository/configuring.rb +156 -0
- data/lib/git/repository/diffing.rb +775 -0
- data/lib/git/repository/inspecting.rb +153 -0
- data/lib/git/repository/logging.rb +247 -0
- data/lib/git/repository/merging.rb +295 -0
- data/lib/git/repository/object_operations.rb +1101 -0
- data/lib/git/repository/path_resolver.rb +207 -0
- data/lib/git/repository/remote_operations.rb +753 -0
- data/lib/git/repository/shared_private.rb +51 -0
- data/lib/git/repository/staging.rb +390 -0
- data/lib/git/repository/stashing.rb +107 -0
- data/lib/git/repository/status_operations.rb +180 -0
- data/lib/git/repository/worktree_operations.rb +159 -0
- data/lib/git/repository.rb +264 -1
- data/lib/git/stash.rb +85 -4
- data/lib/git/stash_info.rb +104 -0
- data/lib/git/stashes.rb +130 -13
- data/lib/git/status.rb +224 -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 +120 -5
- data/lib/git/worktrees.rb +107 -7
- data/lib/git.rb +114 -18
- data/redesign/1_architecture_existing.md +54 -18
- data/redesign/2_architecture_redesign.md +365 -46
- data/redesign/3_architecture_implementation.md +1451 -54
- 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 +349 -20
- data/lib/git/index.rb +0 -6
- 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
|
@@ -0,0 +1,572 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'pathname'
|
|
4
|
+
require 'git/branch'
|
|
5
|
+
require 'git/branch_info'
|
|
6
|
+
require 'git/branches'
|
|
7
|
+
require 'git/commands/branch/create'
|
|
8
|
+
require 'git/commands/branch/delete'
|
|
9
|
+
require 'git/commands/branch/list'
|
|
10
|
+
require 'git/commands/branch/show_current'
|
|
11
|
+
require 'git/commands/checkout/branch'
|
|
12
|
+
require 'git/commands/checkout/files'
|
|
13
|
+
require 'git/commands/checkout_index'
|
|
14
|
+
require 'git/commands/update_ref/update'
|
|
15
|
+
require 'git/parsers/branch'
|
|
16
|
+
require 'git/repository/shared_private'
|
|
17
|
+
|
|
18
|
+
module Git
|
|
19
|
+
class Repository
|
|
20
|
+
# Facade methods for branching operations: creating, checking out, querying,
|
|
21
|
+
# deleting, and updating branches
|
|
22
|
+
#
|
|
23
|
+
# Included by {Git::Repository}.
|
|
24
|
+
#
|
|
25
|
+
# @api public
|
|
26
|
+
#
|
|
27
|
+
module Branching
|
|
28
|
+
# Option keys accepted by {#checkout}
|
|
29
|
+
#
|
|
30
|
+
CHECKOUT_ALLOWED_OPTS = %i[force f new_branch b start_point].freeze
|
|
31
|
+
private_constant :CHECKOUT_ALLOWED_OPTS
|
|
32
|
+
|
|
33
|
+
# Option keys accepted by {#checkout_index}
|
|
34
|
+
#
|
|
35
|
+
CHECKOUT_INDEX_ALLOWED_OPTS = %i[prefix force all path_limiter].freeze
|
|
36
|
+
private_constant :CHECKOUT_INDEX_ALLOWED_OPTS
|
|
37
|
+
|
|
38
|
+
# Returns the name of the current branch
|
|
39
|
+
#
|
|
40
|
+
# @example Get the current branch name
|
|
41
|
+
# repo.current_branch # => "main"
|
|
42
|
+
#
|
|
43
|
+
# @example In detached HEAD state
|
|
44
|
+
# repo.current_branch # => "HEAD"
|
|
45
|
+
#
|
|
46
|
+
# @return [String] the current branch name, or `'HEAD'` when in detached
|
|
47
|
+
# HEAD state
|
|
48
|
+
#
|
|
49
|
+
# @raise [Git::FailedError] if git exits with a non-zero exit status
|
|
50
|
+
#
|
|
51
|
+
def current_branch
|
|
52
|
+
result = Git::Commands::Branch::ShowCurrent.new(@execution_context).call
|
|
53
|
+
name = result.stdout.strip
|
|
54
|
+
name.empty? ? 'HEAD' : name
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Restore working tree files from a tree-ish
|
|
58
|
+
#
|
|
59
|
+
# @example Restore README.md to its HEAD state
|
|
60
|
+
# repo.checkout_file('HEAD', 'README.md')
|
|
61
|
+
#
|
|
62
|
+
# @param version [String] the tree-ish (branch, tag, commit SHA, etc.) to
|
|
63
|
+
# restore the file from
|
|
64
|
+
#
|
|
65
|
+
# @param file [String] the path to the file to restore
|
|
66
|
+
#
|
|
67
|
+
# @return [String] git's stdout from the checkout
|
|
68
|
+
#
|
|
69
|
+
# @raise [Git::FailedError] if git exits with a non-zero exit status
|
|
70
|
+
#
|
|
71
|
+
def checkout_file(version, file)
|
|
72
|
+
Git::Commands::Checkout::Files.new(@execution_context).call(version, pathspec: [file]).stdout
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Switch branches or restore working tree files
|
|
76
|
+
#
|
|
77
|
+
# @example Check out an existing branch
|
|
78
|
+
# repo.checkout('main')
|
|
79
|
+
#
|
|
80
|
+
# @example Create and check out a new branch from main
|
|
81
|
+
# repo.checkout('new-feature', new_branch: true, start_point: 'main')
|
|
82
|
+
#
|
|
83
|
+
# @example Create a new branch with a name different from the start point
|
|
84
|
+
# repo.checkout('main', new_branch: 'new-feature')
|
|
85
|
+
#
|
|
86
|
+
# @example Force checkout discarding local changes
|
|
87
|
+
# repo.checkout('main', force: true)
|
|
88
|
+
#
|
|
89
|
+
# @param branch [String, nil] the branch to check out; defaults to nil
|
|
90
|
+
# (i.e. restore HEAD state)
|
|
91
|
+
#
|
|
92
|
+
# @param options [Hash] options for the checkout command
|
|
93
|
+
#
|
|
94
|
+
# @option options [Boolean, nil] :force (nil) discard local changes when
|
|
95
|
+
# switching branches
|
|
96
|
+
#
|
|
97
|
+
# @option options [Boolean, String, nil] :new_branch (nil) when `true`,
|
|
98
|
+
# creates a new branch named `branch` from `:start_point`
|
|
99
|
+
#
|
|
100
|
+
# When a `String`, creates a new branch with that name, using `branch`
|
|
101
|
+
# as the start point.
|
|
102
|
+
#
|
|
103
|
+
# @option options [Boolean, String, nil] :b (nil) alias for `:new_branch`
|
|
104
|
+
#
|
|
105
|
+
# @option options [Boolean, nil] :f (nil) alias for `:force`
|
|
106
|
+
#
|
|
107
|
+
# @option options [String, nil] :start_point (nil) the commit or branch to
|
|
108
|
+
# start the new branch from; used together with `new_branch: true`
|
|
109
|
+
#
|
|
110
|
+
# @return [String] git's stdout from the checkout
|
|
111
|
+
#
|
|
112
|
+
# @raise [ArgumentError] if unsupported options are provided
|
|
113
|
+
#
|
|
114
|
+
# @raise [Git::FailedError] if git exits with a non-zero exit status
|
|
115
|
+
#
|
|
116
|
+
def checkout(branch = nil, options = {})
|
|
117
|
+
if branch.is_a?(Hash) && options.empty?
|
|
118
|
+
options = branch
|
|
119
|
+
branch = nil
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
SharedPrivate.assert_valid_opts!(CHECKOUT_ALLOWED_OPTS, **options)
|
|
123
|
+
|
|
124
|
+
target, translated_opts = Private.translate_checkout_opts(branch, options)
|
|
125
|
+
Git::Commands::Checkout::Branch.new(@execution_context).call(target, **translated_opts).stdout
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# Populate the working tree from the index
|
|
129
|
+
#
|
|
130
|
+
# @example Check out all files from the index
|
|
131
|
+
# repo.checkout_index(all: true)
|
|
132
|
+
#
|
|
133
|
+
# @example Force check out a specific file
|
|
134
|
+
# repo.checkout_index(force: true, path_limiter: 'README.md')
|
|
135
|
+
#
|
|
136
|
+
# @example Check out files to a staging prefix
|
|
137
|
+
# repo.checkout_index(prefix: 'tmp/stage/', all: true)
|
|
138
|
+
#
|
|
139
|
+
# @param options [Hash] options for the checkout-index command
|
|
140
|
+
#
|
|
141
|
+
# @option options [Boolean, nil] :all (nil) check out all files in the index
|
|
142
|
+
#
|
|
143
|
+
# @option options [Boolean, nil] :force (nil) overwrite existing files
|
|
144
|
+
#
|
|
145
|
+
# @option options [String, nil] :prefix (nil) write files under this path prefix
|
|
146
|
+
# rather than the working directory root
|
|
147
|
+
#
|
|
148
|
+
# @option options [String, Pathname, Array<String, Pathname>, nil] :path_limiter (nil)
|
|
149
|
+
# limit the check out to the given path(s)
|
|
150
|
+
#
|
|
151
|
+
# @return [String] git's stdout from the checkout-index command
|
|
152
|
+
#
|
|
153
|
+
# @raise [ArgumentError] if unsupported options are provided
|
|
154
|
+
#
|
|
155
|
+
# @raise [Git::FailedError] if git exits with a non-zero exit status
|
|
156
|
+
#
|
|
157
|
+
def checkout_index(options = {})
|
|
158
|
+
SharedPrivate.assert_valid_opts!(CHECKOUT_INDEX_ALLOWED_OPTS, **options)
|
|
159
|
+
|
|
160
|
+
paths = Private.normalize_pathspecs(options[:path_limiter], 'path_limiter')
|
|
161
|
+
keyword_opts = options.except(:path_limiter)
|
|
162
|
+
Git::Commands::CheckoutIndex.new(@execution_context).call(*paths.to_a, **keyword_opts).stdout
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# Returns `true` if the named branch exists as a local branch
|
|
166
|
+
#
|
|
167
|
+
# @example Check whether main exists locally
|
|
168
|
+
# repo.local_branch?('main') # => true
|
|
169
|
+
#
|
|
170
|
+
# @param branch [String] the local branch name to look up
|
|
171
|
+
#
|
|
172
|
+
# @return [Boolean] `true` if the branch exists locally, `false` otherwise
|
|
173
|
+
#
|
|
174
|
+
# @raise [Git::FailedError] if git exits with a non-zero exit status
|
|
175
|
+
#
|
|
176
|
+
def local_branch?(branch)
|
|
177
|
+
result = Git::Commands::Branch::List.new(@execution_context).call(branch, format: '%(refname:short)')
|
|
178
|
+
result.stdout.chomp == branch
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
# Returns `true` if the named branch exists as a remote-tracking branch
|
|
182
|
+
#
|
|
183
|
+
# The `branch` argument must be the **short branch name** (e.g. `'master'`),
|
|
184
|
+
# not the combined `remote/branch` form (e.g. `'origin/master'`).
|
|
185
|
+
#
|
|
186
|
+
# @example Check whether master exists on any remote
|
|
187
|
+
# repo.remote_branch?('master') # => true
|
|
188
|
+
#
|
|
189
|
+
# @param branch [String] the short branch name to look up across all remotes
|
|
190
|
+
#
|
|
191
|
+
# @return [Boolean] `true` if a remote-tracking branch with that short name
|
|
192
|
+
# exists, `false` otherwise
|
|
193
|
+
#
|
|
194
|
+
# @raise [Git::FailedError] if git exits with a non-zero exit status
|
|
195
|
+
#
|
|
196
|
+
def remote_branch?(branch)
|
|
197
|
+
result = Git::Commands::Branch::List.new(@execution_context)
|
|
198
|
+
.call("*/#{branch}", remotes: true, format: '%(refname:lstrip=3)')
|
|
199
|
+
result.stdout.each_line.any? { |line| line.chomp == branch }
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
# Returns `true` if the named branch exists locally or as a remote-tracking branch
|
|
203
|
+
#
|
|
204
|
+
# @example Check whether main exists anywhere
|
|
205
|
+
# repo.branch?('main') # => true
|
|
206
|
+
#
|
|
207
|
+
# @param branch [String] the branch name to look up
|
|
208
|
+
#
|
|
209
|
+
# @return [Boolean] `true` if the branch exists locally or remotely,
|
|
210
|
+
# `false` otherwise
|
|
211
|
+
#
|
|
212
|
+
# @raise [Git::FailedError] if git exits with a non-zero exit status
|
|
213
|
+
#
|
|
214
|
+
def branch?(branch)
|
|
215
|
+
local_branch?(branch) || remote_branch?(branch)
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
# Option keys accepted by {#branch_new}
|
|
219
|
+
#
|
|
220
|
+
BRANCH_NEW_ALLOWED_OPTS = %i[].freeze
|
|
221
|
+
private_constant :BRANCH_NEW_ALLOWED_OPTS
|
|
222
|
+
|
|
223
|
+
# Create a new branch
|
|
224
|
+
#
|
|
225
|
+
# @example Create a new branch from the current HEAD
|
|
226
|
+
# repo.branch_new('feature')
|
|
227
|
+
#
|
|
228
|
+
# @example Create a new branch from a specific commit or branch
|
|
229
|
+
# repo.branch_new('feature', 'main')
|
|
230
|
+
#
|
|
231
|
+
# @param branch [String] the name of the branch to create
|
|
232
|
+
#
|
|
233
|
+
# @param start_point [String, nil] the commit, branch, or tag to start the
|
|
234
|
+
# new branch from; defaults to the current HEAD when `nil`
|
|
235
|
+
#
|
|
236
|
+
# @param options [Hash] reserved; must be empty — no options are currently
|
|
237
|
+
# supported
|
|
238
|
+
#
|
|
239
|
+
# @return [void]
|
|
240
|
+
#
|
|
241
|
+
# @raise [ArgumentError] if unsupported options are provided
|
|
242
|
+
#
|
|
243
|
+
# @raise [Git::FailedError] if git exits with a non-zero exit status
|
|
244
|
+
#
|
|
245
|
+
def branch_new(branch, start_point = nil, options = {})
|
|
246
|
+
if start_point.is_a?(Hash) && options.empty?
|
|
247
|
+
options = start_point
|
|
248
|
+
start_point = nil
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
SharedPrivate.assert_valid_opts!(BRANCH_NEW_ALLOWED_OPTS, **options)
|
|
252
|
+
Git::Commands::Branch::Create.new(@execution_context).call(branch, start_point, **options)
|
|
253
|
+
|
|
254
|
+
nil
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
# Option keys accepted by {#branch_delete}
|
|
258
|
+
#
|
|
259
|
+
BRANCH_DELETE_ALLOWED_OPTS = %i[force remotes].freeze
|
|
260
|
+
private_constant :BRANCH_DELETE_ALLOWED_OPTS
|
|
261
|
+
|
|
262
|
+
# Delete one or more local or remote-tracking branches
|
|
263
|
+
#
|
|
264
|
+
# @example Delete a single branch
|
|
265
|
+
# repo.branch_delete('feature') # => "Deleted branch feature (was abc1234)."
|
|
266
|
+
#
|
|
267
|
+
# @example Delete multiple branches at once
|
|
268
|
+
# repo.branch_delete('feature-1', 'feature-2')
|
|
269
|
+
#
|
|
270
|
+
# @example Force-delete an unmerged branch
|
|
271
|
+
# repo.branch_delete('unmerged-branch', force: true)
|
|
272
|
+
#
|
|
273
|
+
# @example Delete a remote-tracking branch
|
|
274
|
+
# repo.branch_delete('origin/feature', remotes: true)
|
|
275
|
+
#
|
|
276
|
+
# @param branches [Array<String>] the name(s) of the branch(es) to delete
|
|
277
|
+
#
|
|
278
|
+
# @param options [Hash] options for the delete command
|
|
279
|
+
#
|
|
280
|
+
# @option options [Boolean, nil] :force (true) allow deleting the branch
|
|
281
|
+
# irrespective of its merged status
|
|
282
|
+
#
|
|
283
|
+
# Defaults to `true` to match the 4.x behavior.
|
|
284
|
+
#
|
|
285
|
+
# @option options [Boolean, nil] :remotes (nil) delete remote-tracking
|
|
286
|
+
# branches
|
|
287
|
+
#
|
|
288
|
+
# Use together with a `remote/branch` name.
|
|
289
|
+
#
|
|
290
|
+
# @return [String] the stdout output from the delete command, e.g.
|
|
291
|
+
# `"Deleted branch feature (was abc1234)."`
|
|
292
|
+
#
|
|
293
|
+
# @raise [ArgumentError] if unsupported options are provided
|
|
294
|
+
#
|
|
295
|
+
# @raise [Git::FailedError] if git exits outside the allowed range (exit code > 1)
|
|
296
|
+
#
|
|
297
|
+
# @raise [Git::Error] if git reports a deletion failure
|
|
298
|
+
#
|
|
299
|
+
def branch_delete(*branches, **options)
|
|
300
|
+
options = { force: true }.merge(options)
|
|
301
|
+
SharedPrivate.assert_valid_opts!(BRANCH_DELETE_ALLOWED_OPTS, **options)
|
|
302
|
+
|
|
303
|
+
result = Git::Commands::Branch::Delete.new(@execution_context).call(*branches, **options)
|
|
304
|
+
|
|
305
|
+
raise Git::Error, result.stderr.strip unless result.status.success?
|
|
306
|
+
|
|
307
|
+
result.stdout.strip
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
# Returns the `git branch --list --contains` stdout for a given commit
|
|
311
|
+
#
|
|
312
|
+
# The output format is the human-readable `git branch` listing: each
|
|
313
|
+
# matching branch name appears on its own line, prefixed with two spaces,
|
|
314
|
+
# or `* ` if it is the currently checked-out branch. This is the same
|
|
315
|
+
# format returned by `Git::Lib#branch_contains` in the 4.x gem series.
|
|
316
|
+
#
|
|
317
|
+
# @example List all branches that contain a commit
|
|
318
|
+
# repo.branch_contains('abc1234')
|
|
319
|
+
# # => " main\n"
|
|
320
|
+
#
|
|
321
|
+
# @example The current branch is marked with an asterisk
|
|
322
|
+
# repo.branch_contains('abc1234')
|
|
323
|
+
# # => "* main\n feature\n"
|
|
324
|
+
#
|
|
325
|
+
# @example Limit the search to branches matching a shell wildcard pattern
|
|
326
|
+
# repo.branch_contains('abc1234', 'feature/*')
|
|
327
|
+
#
|
|
328
|
+
# @example Typical usage: check whether any branch contains the commit
|
|
329
|
+
# repo.branch_contains('abc1234').empty? # => false
|
|
330
|
+
#
|
|
331
|
+
# @param commit [String] the commit SHA or ref to look up
|
|
332
|
+
#
|
|
333
|
+
# @param branch_name [String, nil] a shell wildcard pattern to limit which
|
|
334
|
+
# branches are searched
|
|
335
|
+
#
|
|
336
|
+
# When empty or `nil`, all local branches are searched.
|
|
337
|
+
#
|
|
338
|
+
# @return [String] the `git branch --list --contains` stdout
|
|
339
|
+
#
|
|
340
|
+
# Each matching branch appears on its own line, prefixed with two
|
|
341
|
+
# spaces, or `* ` for the currently checked-out branch. Returns an
|
|
342
|
+
# empty string when no matching branch contains the commit.
|
|
343
|
+
#
|
|
344
|
+
# @raise [Git::FailedError] if git exits with a non-zero exit status
|
|
345
|
+
#
|
|
346
|
+
def branch_contains(commit, branch_name = '')
|
|
347
|
+
branch_name = branch_name.to_s
|
|
348
|
+
pattern = branch_name.empty? ? nil : branch_name
|
|
349
|
+
Git::Commands::Branch::List.new(@execution_context)
|
|
350
|
+
.call(*[pattern].compact, contains: commit, no_color: true)
|
|
351
|
+
.stdout
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
# Returns all local and remote-tracking branches as structured objects
|
|
355
|
+
#
|
|
356
|
+
# @example List all branches
|
|
357
|
+
# repo.branches_all
|
|
358
|
+
# # => [#<data Git::BranchInfo refname="main", current=true, ...>,
|
|
359
|
+
# # #<data Git::BranchInfo refname="remotes/origin/main", current=false, ...>]
|
|
360
|
+
#
|
|
361
|
+
# @example Find the currently checked-out branch
|
|
362
|
+
# repo.branches_all.find(&:current)
|
|
363
|
+
#
|
|
364
|
+
# @example List only local branches
|
|
365
|
+
# repo.branches_all.reject(&:remote?)
|
|
366
|
+
#
|
|
367
|
+
# @return [Array<Git::BranchInfo>] parsed branch information for every
|
|
368
|
+
# local and remote-tracking branch
|
|
369
|
+
#
|
|
370
|
+
# Returns an empty array when the repository has no branches.
|
|
371
|
+
#
|
|
372
|
+
# @raise [Git::FailedError] if git exits with a non-zero exit status
|
|
373
|
+
#
|
|
374
|
+
def branches_all
|
|
375
|
+
result = Git::Commands::Branch::List.new(@execution_context).call(
|
|
376
|
+
all: true, format: Git::Parsers::Branch::FORMAT_STRING
|
|
377
|
+
)
|
|
378
|
+
Git::Parsers::Branch.parse_list(result.stdout)
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
# Update a branch ref to point to a new commit
|
|
382
|
+
#
|
|
383
|
+
# Derives the full ref from the `branch` argument:
|
|
384
|
+
#
|
|
385
|
+
# - `remotes/<remote>/<name>` or `refs/remotes/<remote>/<name>` →
|
|
386
|
+
# writes to `refs/remotes/<remote>/<name>` (remote-tracking branch)
|
|
387
|
+
# - Any other value → writes to `refs/heads/<branch>` (local branch)
|
|
388
|
+
#
|
|
389
|
+
# @example Advance a local branch to the current HEAD
|
|
390
|
+
# repo.update_ref('feature', repo.rev_parse('HEAD'))
|
|
391
|
+
#
|
|
392
|
+
# @example Reset a local branch to an older commit
|
|
393
|
+
# repo.update_ref('main', 'abc1234def5678')
|
|
394
|
+
#
|
|
395
|
+
# @example Update a remote-tracking branch ref
|
|
396
|
+
# repo.update_ref('remotes/origin/main', 'abc1234def5678')
|
|
397
|
+
#
|
|
398
|
+
# @param branch [String] a local or remote-tracking branch name
|
|
399
|
+
#
|
|
400
|
+
# Short local names (e.g. `'main'`) resolve to `refs/heads/<branch>`.
|
|
401
|
+
# Remote-tracking names with a `remotes/<remote>/` or
|
|
402
|
+
# `refs/remotes/<remote>/` prefix (e.g. `'remotes/origin/main'`)
|
|
403
|
+
# resolve to `refs/remotes/<remote>/<name>`.
|
|
404
|
+
#
|
|
405
|
+
# @param commit [String] the commit SHA to point the branch at
|
|
406
|
+
#
|
|
407
|
+
# @return [Git::CommandLineResult] the result of calling `git update-ref`
|
|
408
|
+
#
|
|
409
|
+
# @raise [Git::FailedError] if git exits with a non-zero exit status
|
|
410
|
+
#
|
|
411
|
+
def update_ref(branch, commit)
|
|
412
|
+
ref = Private.build_update_ref(branch)
|
|
413
|
+
Git::Commands::UpdateRef::Update.new(@execution_context).call(ref, commit)
|
|
414
|
+
end
|
|
415
|
+
|
|
416
|
+
# Returns a {Git::Branch} object for the given branch name
|
|
417
|
+
#
|
|
418
|
+
# @example Get a branch object for 'main'
|
|
419
|
+
# repo.branch('main') #=> #<Git::Branch 'main'>
|
|
420
|
+
#
|
|
421
|
+
# @example Get a branch object for the current branch
|
|
422
|
+
# repo.branch #=> #<Git::Branch 'main'>
|
|
423
|
+
#
|
|
424
|
+
# @param branch_name [String] the branch name (defaults to the current branch)
|
|
425
|
+
#
|
|
426
|
+
# @return [Git::Branch] the branch object
|
|
427
|
+
#
|
|
428
|
+
# @raise [Git::FailedError] if git exits with a non-zero exit status
|
|
429
|
+
#
|
|
430
|
+
def branch(branch_name = current_branch)
|
|
431
|
+
branch_info = Git::BranchInfo.new(
|
|
432
|
+
refname: branch_name,
|
|
433
|
+
target_oid: nil,
|
|
434
|
+
current: false,
|
|
435
|
+
worktree: false,
|
|
436
|
+
symref: nil,
|
|
437
|
+
upstream: nil
|
|
438
|
+
)
|
|
439
|
+
Git::Branch.new(self, branch_info)
|
|
440
|
+
end
|
|
441
|
+
|
|
442
|
+
# Returns a {Git::Branches} collection of all branches in the repository
|
|
443
|
+
#
|
|
444
|
+
# @example List all branches
|
|
445
|
+
# repo.branches
|
|
446
|
+
# # => #<Git::Branches ...>
|
|
447
|
+
#
|
|
448
|
+
# @example Iterate over all branches
|
|
449
|
+
# repo.branches.each { |b| puts b.name }
|
|
450
|
+
#
|
|
451
|
+
# @example Access local branches only
|
|
452
|
+
# repo.branches.local
|
|
453
|
+
#
|
|
454
|
+
# @example Access remote-tracking branches only
|
|
455
|
+
# repo.branches.remote
|
|
456
|
+
#
|
|
457
|
+
# @example Look up a branch by name
|
|
458
|
+
# repo.branches['main'] # => #<Git::Branch 'main'>
|
|
459
|
+
#
|
|
460
|
+
# @return [Git::Branches] a collection wrapping all local and
|
|
461
|
+
# remote-tracking branches in the repository
|
|
462
|
+
#
|
|
463
|
+
# @raise [Git::FailedError] if git exits with a non-zero exit status
|
|
464
|
+
#
|
|
465
|
+
def branches
|
|
466
|
+
Git::Branches.new(self)
|
|
467
|
+
end
|
|
468
|
+
|
|
469
|
+
# Private helpers local to {Git::Repository::Branching}
|
|
470
|
+
#
|
|
471
|
+
# @api private
|
|
472
|
+
module Private
|
|
473
|
+
module_function
|
|
474
|
+
|
|
475
|
+
# Translates legacy checkout options to the new command interface
|
|
476
|
+
#
|
|
477
|
+
# Legacy callers passed combinations like:
|
|
478
|
+
# checkout('branch', new_branch: true, start_point: 'main')
|
|
479
|
+
# which should map to:
|
|
480
|
+
# checkout('main', b: 'branch')
|
|
481
|
+
#
|
|
482
|
+
# @param branch [String, nil] the branch argument passed to {#checkout}
|
|
483
|
+
#
|
|
484
|
+
# @param options [Hash] the raw options passed to {#checkout}
|
|
485
|
+
#
|
|
486
|
+
# @return [Array] a two-element tuple `[target, options]` containing the
|
|
487
|
+
# translated checkout arguments
|
|
488
|
+
#
|
|
489
|
+
# `target` (`String` or `nil`) is the branch or commit to check out.
|
|
490
|
+
# `options` is a `Hash` of keyword arguments for
|
|
491
|
+
# `Git::Commands::Checkout::Branch#call`
|
|
492
|
+
#
|
|
493
|
+
# @api private
|
|
494
|
+
#
|
|
495
|
+
def translate_checkout_opts(branch, options)
|
|
496
|
+
if options[:new_branch] == true || options[:b] == true
|
|
497
|
+
[options[:start_point], options.except(:new_branch, :b, :start_point).merge(b: branch)]
|
|
498
|
+
elsif options[:new_branch].is_a?(String)
|
|
499
|
+
[branch, options.except(:new_branch).merge(b: options[:new_branch])]
|
|
500
|
+
else
|
|
501
|
+
[branch, options]
|
|
502
|
+
end
|
|
503
|
+
end
|
|
504
|
+
|
|
505
|
+
# Normalizes path specifications for Git commands
|
|
506
|
+
#
|
|
507
|
+
# @param pathspecs [String, Pathname, Array<String, Pathname>, nil]
|
|
508
|
+
# the path(s) to normalize
|
|
509
|
+
#
|
|
510
|
+
# @param arg_name [String] the argument name used in error messages
|
|
511
|
+
#
|
|
512
|
+
# @return [Array<String>, nil] the normalized paths, or `nil` if none are valid
|
|
513
|
+
#
|
|
514
|
+
# @raise [ArgumentError] when any path is not a `String` or `Pathname`
|
|
515
|
+
#
|
|
516
|
+
# @api private
|
|
517
|
+
#
|
|
518
|
+
def normalize_pathspecs(pathspecs, arg_name)
|
|
519
|
+
return nil unless pathspecs
|
|
520
|
+
|
|
521
|
+
normalized = Array(pathspecs)
|
|
522
|
+
validate_pathspec_types(normalized, arg_name)
|
|
523
|
+
|
|
524
|
+
normalized = normalized.map(&:to_s).reject(&:empty?)
|
|
525
|
+
return nil if normalized.empty?
|
|
526
|
+
|
|
527
|
+
normalized
|
|
528
|
+
end
|
|
529
|
+
|
|
530
|
+
# Raises an error if any element of `pathspecs` is not a `String` or `Pathname`
|
|
531
|
+
#
|
|
532
|
+
# @param pathspecs [Array] the path elements to validate
|
|
533
|
+
#
|
|
534
|
+
# @param arg_name [String] the argument name used in error messages
|
|
535
|
+
#
|
|
536
|
+
# @return [void]
|
|
537
|
+
#
|
|
538
|
+
# @raise [ArgumentError] when any element is not a `String` or `Pathname`
|
|
539
|
+
#
|
|
540
|
+
# @api private
|
|
541
|
+
#
|
|
542
|
+
def validate_pathspec_types(pathspecs, arg_name)
|
|
543
|
+
return if pathspecs.all? { |path| path.is_a?(String) || path.is_a?(Pathname) }
|
|
544
|
+
|
|
545
|
+
raise ArgumentError, "Invalid #{arg_name}: must be a String, Pathname, or Array of Strings/Pathnames"
|
|
546
|
+
end
|
|
547
|
+
|
|
548
|
+
# Builds the full git ref string from a branch name argument
|
|
549
|
+
#
|
|
550
|
+
# Mirrors the routing logic of `Git::Branch#update_ref` for backward
|
|
551
|
+
# compatibility:
|
|
552
|
+
#
|
|
553
|
+
# - `remotes/<remote>/<name>` or `refs/remotes/<remote>/<name>` →
|
|
554
|
+
# `refs/remotes/<remote>/<name>`
|
|
555
|
+
# - Any other value → `refs/heads/<branch>`
|
|
556
|
+
#
|
|
557
|
+
# @param branch [String] a short local branch name or a remote-tracking
|
|
558
|
+
# branch name with a `remotes/` or `refs/remotes/` prefix
|
|
559
|
+
#
|
|
560
|
+
# @return [String] the full git ref string
|
|
561
|
+
#
|
|
562
|
+
# @api private
|
|
563
|
+
#
|
|
564
|
+
def build_update_ref(branch)
|
|
565
|
+
match = branch.match(%r{\A(?:refs/)?remotes/([^/]+)/(.+)\z})
|
|
566
|
+
match ? "refs/remotes/#{match[1]}/#{match[2]}" : "refs/heads/#{branch}"
|
|
567
|
+
end
|
|
568
|
+
end
|
|
569
|
+
private_constant :Private
|
|
570
|
+
end
|
|
571
|
+
end
|
|
572
|
+
end
|