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,153 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'git/commands/fsck'
|
|
4
|
+
require 'git/commands/show'
|
|
5
|
+
require 'git/parsers/fsck'
|
|
6
|
+
require 'git/repository/shared_private'
|
|
7
|
+
|
|
8
|
+
module Git
|
|
9
|
+
class Repository
|
|
10
|
+
# Facade methods for read-only repository inspection operations
|
|
11
|
+
#
|
|
12
|
+
# These methods report on the contents and integrity of the repository.
|
|
13
|
+
#
|
|
14
|
+
# Included by {Git::Repository}.
|
|
15
|
+
#
|
|
16
|
+
# @api public
|
|
17
|
+
#
|
|
18
|
+
module Inspecting
|
|
19
|
+
# Show a single git object (a commit, tag, tree, or blob)
|
|
20
|
+
#
|
|
21
|
+
# @example Show the HEAD commit
|
|
22
|
+
# repo.show
|
|
23
|
+
#
|
|
24
|
+
# @example Show a specific commit
|
|
25
|
+
# repo.show('HEAD~1')
|
|
26
|
+
#
|
|
27
|
+
# @example Show the contents of a file at a revision
|
|
28
|
+
# repo.show('HEAD', 'README.md')
|
|
29
|
+
#
|
|
30
|
+
# @param objectish [String, nil] the object to show; a ref, SHA, or
|
|
31
|
+
# `objectish:path` expression
|
|
32
|
+
#
|
|
33
|
+
# Defaults to `HEAD` when `nil`.
|
|
34
|
+
#
|
|
35
|
+
# @param path [String, nil] the file whose contents to show at `objectish`,
|
|
36
|
+
# when given
|
|
37
|
+
#
|
|
38
|
+
# Combined with `objectish` as `objectish:path`. When `objectish` is `nil`
|
|
39
|
+
# and `path` is given, `HEAD` is used as the objectish, so
|
|
40
|
+
# `show(nil, 'README.md')` resolves to `HEAD:README.md`.
|
|
41
|
+
#
|
|
42
|
+
# @return [String] git's stdout from the show, with trailing newlines
|
|
43
|
+
# preserved
|
|
44
|
+
#
|
|
45
|
+
# @raise [Git::FailedError] when git exits with a non-zero exit status
|
|
46
|
+
#
|
|
47
|
+
def show(objectish = nil, path = nil)
|
|
48
|
+
object = path ? "#{objectish || 'HEAD'}:#{path}" : objectish
|
|
49
|
+
Git::Commands::Show.new(@execution_context).call(*[object].compact).stdout
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Option keys accepted by {#fsck}
|
|
53
|
+
#
|
|
54
|
+
# `:progress`/`:no_progress` are intentionally excluded: progress output is
|
|
55
|
+
# always suppressed (see {#fsck}), so callers may not toggle it.
|
|
56
|
+
FSCK_ALLOWED_OPTS = %i[
|
|
57
|
+
tags root unreachable cache no_reflogs
|
|
58
|
+
full no_full strict verbose lost_found dangling no_dangling
|
|
59
|
+
connectivity_only name_objects no_name_objects references no_references
|
|
60
|
+
].freeze
|
|
61
|
+
private_constant :FSCK_ALLOWED_OPTS
|
|
62
|
+
|
|
63
|
+
# Verify the connectivity and validity of the objects in the database
|
|
64
|
+
#
|
|
65
|
+
# Runs `git fsck` and returns the categorized objects it flags. Progress
|
|
66
|
+
# output is always suppressed (`--no-progress`) so that stdout contains only
|
|
67
|
+
# the machine-parsable findings.
|
|
68
|
+
#
|
|
69
|
+
# @overload fsck(*objects, **options)
|
|
70
|
+
#
|
|
71
|
+
# @example Check repository integrity
|
|
72
|
+
# result = repo.fsck
|
|
73
|
+
# result.dangling.each { |obj| puts "#{obj.type}: #{obj.oid}" }
|
|
74
|
+
#
|
|
75
|
+
# @example Check if the repository is clean
|
|
76
|
+
# repo.fsck.empty? #=> true
|
|
77
|
+
#
|
|
78
|
+
# @example List root commits
|
|
79
|
+
# repo.fsck(root: true).root.each { |obj| puts obj.oid }
|
|
80
|
+
#
|
|
81
|
+
# @example Check specific objects
|
|
82
|
+
# repo.fsck('abc1234', 'def5678')
|
|
83
|
+
#
|
|
84
|
+
# @param objects [Array<String>] specific objects to treat as heads for the
|
|
85
|
+
# unreachability trace
|
|
86
|
+
#
|
|
87
|
+
# When none are given, git fsck defaults to the index file, all refs, and
|
|
88
|
+
# all reflogs.
|
|
89
|
+
#
|
|
90
|
+
# @param options [Hash] options for the fsck command
|
|
91
|
+
#
|
|
92
|
+
# @option options [Boolean, nil] :tags (nil) report tags
|
|
93
|
+
#
|
|
94
|
+
# @option options [Boolean, nil] :root (nil) report root nodes
|
|
95
|
+
#
|
|
96
|
+
# @option options [Boolean, nil] :unreachable (nil) print objects that exist
|
|
97
|
+
# but are not reachable from any reference node
|
|
98
|
+
#
|
|
99
|
+
# @option options [Boolean, nil] :cache (nil) consider objects recorded in the
|
|
100
|
+
# index as head nodes for reachability
|
|
101
|
+
#
|
|
102
|
+
# @option options [Boolean, nil] :no_reflogs (nil) do not consider commits
|
|
103
|
+
# referenced only by reflogs to be reachable
|
|
104
|
+
#
|
|
105
|
+
# @option options [Boolean, nil] :full (nil) also check alternate object
|
|
106
|
+
# pools and packed archives, not just the local store
|
|
107
|
+
#
|
|
108
|
+
# @option options [Boolean, nil] :no_full (nil) skip alternate object pools and
|
|
109
|
+
# packed archives
|
|
110
|
+
#
|
|
111
|
+
# @option options [Boolean, nil] :strict (nil) enable stricter checking
|
|
112
|
+
#
|
|
113
|
+
# @option options [Boolean, nil] :verbose (nil) be chatty
|
|
114
|
+
#
|
|
115
|
+
# @option options [Boolean, nil] :lost_found (nil) write dangling objects
|
|
116
|
+
# into `.git/lost-found`
|
|
117
|
+
#
|
|
118
|
+
# This modifies the repository by creating files.
|
|
119
|
+
#
|
|
120
|
+
# @option options [Boolean, nil] :dangling (nil) print dangling objects
|
|
121
|
+
#
|
|
122
|
+
# @option options [Boolean, nil] :no_dangling (nil) suppress dangling object
|
|
123
|
+
# reporting
|
|
124
|
+
#
|
|
125
|
+
# @option options [Boolean, nil] :connectivity_only (nil) check only
|
|
126
|
+
# connectivity; faster but does not validate blob content
|
|
127
|
+
#
|
|
128
|
+
# @option options [Boolean, nil] :name_objects (nil) show the name of each
|
|
129
|
+
# reachable object alongside its identifier
|
|
130
|
+
#
|
|
131
|
+
# @option options [Boolean, nil] :no_name_objects (nil) suppress object name
|
|
132
|
+
# display
|
|
133
|
+
#
|
|
134
|
+
# @option options [Boolean, nil] :references (nil) check reference database
|
|
135
|
+
# consistency
|
|
136
|
+
#
|
|
137
|
+
# @option options [Boolean, nil] :no_references (nil) skip reference checking
|
|
138
|
+
#
|
|
139
|
+
# @return [Git::FsckResult] the objects flagged by fsck, categorized by status
|
|
140
|
+
#
|
|
141
|
+
# @raise [ArgumentError] when unsupported options are provided
|
|
142
|
+
#
|
|
143
|
+
# @raise [Git::FailedError] when git exits outside the allowed range (exit
|
|
144
|
+
# code > 7)
|
|
145
|
+
#
|
|
146
|
+
def fsck(*objects, **)
|
|
147
|
+
SharedPrivate.assert_valid_opts!(FSCK_ALLOWED_OPTS, **)
|
|
148
|
+
result = Git::Commands::Fsck.new(@execution_context).call(*objects, **, no_progress: true)
|
|
149
|
+
Git::Parsers::Fsck.parse(result.stdout)
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
end
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'git/commands/log'
|
|
4
|
+
require 'git/log'
|
|
5
|
+
require 'git/repository/shared_private'
|
|
6
|
+
|
|
7
|
+
module Git
|
|
8
|
+
class Repository
|
|
9
|
+
# Facade methods for querying commit history
|
|
10
|
+
#
|
|
11
|
+
# Included by {Git::Repository}.
|
|
12
|
+
#
|
|
13
|
+
# @api public
|
|
14
|
+
#
|
|
15
|
+
module Logging
|
|
16
|
+
FULL_LOG_COMMITS_ALLOWED_OPTS = %i[
|
|
17
|
+
count all cherry since until grep author between object path_limiter skip merges
|
|
18
|
+
].freeze
|
|
19
|
+
private_constant :FULL_LOG_COMMITS_ALLOWED_OPTS
|
|
20
|
+
|
|
21
|
+
# Returns commits within the given revision range
|
|
22
|
+
#
|
|
23
|
+
# @overload full_log_commits(opts = {})
|
|
24
|
+
#
|
|
25
|
+
# @example Return commits from all refs
|
|
26
|
+
# repo.full_log_commits(all: true).first['sha']
|
|
27
|
+
# #=> "a1b2c3d4..."
|
|
28
|
+
#
|
|
29
|
+
# @example Return commits between two revisions
|
|
30
|
+
# repo.full_log_commits(between: ['v1.0.0', 'HEAD']).map { |c| c['sha'] }
|
|
31
|
+
# #=> ["d4e5f6...", "a1b2c3..."]
|
|
32
|
+
#
|
|
33
|
+
# @param opts [Hash] options for the log query
|
|
34
|
+
#
|
|
35
|
+
# @option opts [Integer, nil] :count (nil) maximum number of commits to return
|
|
36
|
+
#
|
|
37
|
+
# @option opts [Boolean, nil] :all (nil) include commits reachable from any ref
|
|
38
|
+
#
|
|
39
|
+
# @option opts [Boolean, nil] :cherry (nil) omit commits equivalent to cherry-picked commits
|
|
40
|
+
#
|
|
41
|
+
# @option opts [String] :since (nil) include commits newer than this date expression
|
|
42
|
+
#
|
|
43
|
+
# @option opts [String] :until (nil) include commits older than this date expression
|
|
44
|
+
#
|
|
45
|
+
# @option opts [String] :grep (nil) only include commits whose message matches this pattern
|
|
46
|
+
#
|
|
47
|
+
# @option opts [String] :author (nil) only include commits whose author matches this pattern
|
|
48
|
+
#
|
|
49
|
+
# @option opts [Array<String>] :between (nil) revision range as two commit-ish values
|
|
50
|
+
#
|
|
51
|
+
# When both `:between` and `:object` are provided, `:between` takes precedence.
|
|
52
|
+
#
|
|
53
|
+
# @option opts [String] :object (nil) single revision range expression for git log
|
|
54
|
+
#
|
|
55
|
+
# Ignored when `:between` is provided.
|
|
56
|
+
#
|
|
57
|
+
# @option opts [String, Pathname, Array<String, Pathname>] :path_limiter (nil)
|
|
58
|
+
# only include commits that impact files from the specified path(s)
|
|
59
|
+
#
|
|
60
|
+
# @option opts [Integer] :skip (nil) skip this many commits before output
|
|
61
|
+
#
|
|
62
|
+
# @option opts [Boolean, nil] :merges (nil) include only merge commits
|
|
63
|
+
#
|
|
64
|
+
# @return [Array<Hash>] the parsed raw log output for each commit
|
|
65
|
+
#
|
|
66
|
+
# @raise [ArgumentError] if unsupported options are provided
|
|
67
|
+
#
|
|
68
|
+
# @raise [ArgumentError] if `:count` is not an Integer
|
|
69
|
+
#
|
|
70
|
+
# @raise [Git::FailedError] if git exits with a non-zero exit status
|
|
71
|
+
#
|
|
72
|
+
# @see https://git-scm.com/docs/git-log git-log
|
|
73
|
+
#
|
|
74
|
+
def full_log_commits(opts = {})
|
|
75
|
+
SharedPrivate.assert_valid_opts!(FULL_LOG_COMMITS_ALLOWED_OPTS, **opts)
|
|
76
|
+
Private.validate_log_count_option!(opts)
|
|
77
|
+
Private.validate_log_between_option!(opts)
|
|
78
|
+
|
|
79
|
+
call_opts = Private.log_base_call_options(opts, skip: opts[:skip], merges: opts[:merges])
|
|
80
|
+
revision_range_args = Private.log_revision_range_args(opts)
|
|
81
|
+
Private.run_log_command(@execution_context, revision_range_args, call_opts)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Returns a new {Git::Log} query builder scoped to this repository
|
|
85
|
+
#
|
|
86
|
+
# @example Build a log query and execute it
|
|
87
|
+
# results = repo.log(50).author('Alice').since('2 weeks ago').execute
|
|
88
|
+
# results.each { |commit| puts commit.sha }
|
|
89
|
+
#
|
|
90
|
+
# @param count [Integer, Symbol, nil] the maximum number of commits to return,
|
|
91
|
+
# or `:all` / `nil` to return all commits; passed directly to {Git::Log#initialize}
|
|
92
|
+
#
|
|
93
|
+
# @return [Git::Log] a new log query builder
|
|
94
|
+
#
|
|
95
|
+
# @see Git::Log
|
|
96
|
+
#
|
|
97
|
+
def log(count = 30)
|
|
98
|
+
Git::Log.new(self, count)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Internal helpers for {Logging} that should not be mixed into
|
|
102
|
+
# {Git::Repository} instances
|
|
103
|
+
#
|
|
104
|
+
# @api private
|
|
105
|
+
#
|
|
106
|
+
module Private
|
|
107
|
+
module_function
|
|
108
|
+
|
|
109
|
+
def validate_log_count_option!(opts)
|
|
110
|
+
return if opts[:count].nil? || opts[:count].is_a?(Integer)
|
|
111
|
+
|
|
112
|
+
raise ArgumentError, "The log count option must be an Integer but was #{opts[:count].inspect}"
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def validate_log_between_option!(opts)
|
|
116
|
+
between = opts[:between]
|
|
117
|
+
return if between.nil?
|
|
118
|
+
return if between.is_a?(Array) && between.length == 2 && between.none?(&:nil?)
|
|
119
|
+
|
|
120
|
+
raise ArgumentError,
|
|
121
|
+
"The log between option must be an Array with exactly two non-nil values but was #{between.inspect}"
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def log_revision_range_args(opts)
|
|
125
|
+
if opts[:between]
|
|
126
|
+
["#{opts[:between][0]}..#{opts[:between][1]}"]
|
|
127
|
+
elsif opts[:object].is_a?(String)
|
|
128
|
+
[opts[:object]]
|
|
129
|
+
else
|
|
130
|
+
[]
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def log_base_call_options(opts, extra = {})
|
|
135
|
+
{
|
|
136
|
+
all: opts[:all],
|
|
137
|
+
cherry: opts[:cherry],
|
|
138
|
+
since: opts[:since],
|
|
139
|
+
until: opts[:until],
|
|
140
|
+
grep: opts[:grep],
|
|
141
|
+
author: opts[:author],
|
|
142
|
+
max_count: opts[:count],
|
|
143
|
+
path: opts[:path_limiter] ? Array(opts[:path_limiter]) : nil
|
|
144
|
+
}.merge(extra).compact
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def run_log_command(execution_context, revision_range_args, call_opts)
|
|
148
|
+
log_or_empty_on_unborn do
|
|
149
|
+
result = Git::Commands::Log.new(execution_context).call(
|
|
150
|
+
*revision_range_args,
|
|
151
|
+
no_color: true,
|
|
152
|
+
pretty: 'raw',
|
|
153
|
+
**call_opts
|
|
154
|
+
)
|
|
155
|
+
RawLogParser.new(result.stdout.split("\n")).parse
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def log_or_empty_on_unborn
|
|
160
|
+
yield
|
|
161
|
+
rescue Git::FailedError => e
|
|
162
|
+
raise unless e.result.status.exitstatus == 128 &&
|
|
163
|
+
e.result.stderr =~ /does not have any commits yet/
|
|
164
|
+
|
|
165
|
+
[]
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# Parser for `git log --pretty=raw` output into commit hashes
|
|
169
|
+
#
|
|
170
|
+
# @api private
|
|
171
|
+
#
|
|
172
|
+
class RawLogParser
|
|
173
|
+
def initialize(lines)
|
|
174
|
+
@lines = lines
|
|
175
|
+
@commits = []
|
|
176
|
+
@current_commit = nil
|
|
177
|
+
@in_message = false
|
|
178
|
+
@last_metadata_key = nil
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
# Parse raw `git log --pretty=raw` lines into commit hashes
|
|
182
|
+
#
|
|
183
|
+
# @return [Array<Hash>] the parsed commits in command output order
|
|
184
|
+
#
|
|
185
|
+
def parse
|
|
186
|
+
@lines.each { |line| process_line(line.chomp) }
|
|
187
|
+
finalize_commit
|
|
188
|
+
@commits
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
private
|
|
192
|
+
|
|
193
|
+
def process_line(line)
|
|
194
|
+
if line.empty?
|
|
195
|
+
@in_message = !@in_message
|
|
196
|
+
return
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
@in_message = false if @in_message && !line.start_with?(' ')
|
|
200
|
+
|
|
201
|
+
@in_message ? process_message_line(line) : process_metadata_line(line)
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
def process_message_line(line)
|
|
205
|
+
@current_commit['message'] << "#{line[4..]}\n"
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def process_metadata_line(line)
|
|
209
|
+
if line.start_with?(' ') && @last_metadata_key
|
|
210
|
+
@current_commit[@last_metadata_key] << "\n#{line[1..]}"
|
|
211
|
+
return
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
key, *value = line.split
|
|
215
|
+
value = value.join(' ')
|
|
216
|
+
@last_metadata_key = nil
|
|
217
|
+
dispatch_metadata_key(key, value)
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
def dispatch_metadata_key(key, value)
|
|
221
|
+
case key
|
|
222
|
+
when 'commit'
|
|
223
|
+
start_new_commit(value)
|
|
224
|
+
when 'parent'
|
|
225
|
+
@current_commit['parent'] << value
|
|
226
|
+
else
|
|
227
|
+
@current_commit[key] = value
|
|
228
|
+
@last_metadata_key = key
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
def start_new_commit(sha)
|
|
233
|
+
finalize_commit
|
|
234
|
+
@current_commit = { 'sha' => sha, 'message' => +'', 'parent' => [] }
|
|
235
|
+
@last_metadata_key = nil
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
def finalize_commit
|
|
239
|
+
@commits << @current_commit if @current_commit
|
|
240
|
+
end
|
|
241
|
+
end
|
|
242
|
+
private_constant :RawLogParser
|
|
243
|
+
end
|
|
244
|
+
private_constant :Private
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
end
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'tempfile'
|
|
4
|
+
require 'git/commands/diff'
|
|
5
|
+
require 'git/commands/merge/start'
|
|
6
|
+
require 'git/commands/merge_base'
|
|
7
|
+
require 'git/commands/revert/start'
|
|
8
|
+
require 'git/commands/show'
|
|
9
|
+
require 'git/repository/shared_private'
|
|
10
|
+
|
|
11
|
+
module Git
|
|
12
|
+
class Repository
|
|
13
|
+
# Facade methods for merge operations: merging branches into the current branch,
|
|
14
|
+
# and finding common ancestors between commits
|
|
15
|
+
#
|
|
16
|
+
# Included by {Git::Repository}.
|
|
17
|
+
#
|
|
18
|
+
# @api public
|
|
19
|
+
#
|
|
20
|
+
module Merging
|
|
21
|
+
# Option keys accepted by {#merge}
|
|
22
|
+
#
|
|
23
|
+
# Derived from the 4.x option map for `Git::Lib#merge`.
|
|
24
|
+
MERGE_ALLOWED_OPTS = %i[no_commit no_ff m message].freeze
|
|
25
|
+
private_constant :MERGE_ALLOWED_OPTS
|
|
26
|
+
|
|
27
|
+
# Option keys accepted by {#merge_base}
|
|
28
|
+
#
|
|
29
|
+
# Derived from the 4.x option map for `Git::Lib#merge_base`.
|
|
30
|
+
MERGE_BASE_ALLOWED_OPTS = %i[octopus independent fork_point all].freeze
|
|
31
|
+
private_constant :MERGE_BASE_ALLOWED_OPTS
|
|
32
|
+
|
|
33
|
+
# Merge one or more branches into the current branch
|
|
34
|
+
#
|
|
35
|
+
# The merge commit message may be given by the message positional argument, the
|
|
36
|
+
# `:message` option, or the `:m` option; if more than one is provided, the
|
|
37
|
+
# precedence is positional argument > `:message` > `:m`.
|
|
38
|
+
#
|
|
39
|
+
# @example Merge a single branch
|
|
40
|
+
# repo.merge('feature')
|
|
41
|
+
#
|
|
42
|
+
# @example Merge a branch with a no-fast-forward commit message
|
|
43
|
+
# repo.merge('feature', 'Merge feature into main', no_ff: true)
|
|
44
|
+
#
|
|
45
|
+
# @example Octopus merge of multiple branches
|
|
46
|
+
# repo.merge(%w[feature-a feature-b])
|
|
47
|
+
#
|
|
48
|
+
# @example Merge without committing
|
|
49
|
+
# repo.merge('feature', nil, no_commit: true)
|
|
50
|
+
#
|
|
51
|
+
# @param branch [String, Array<String>, #to_s] the branch or branches to merge
|
|
52
|
+
# into the current branch
|
|
53
|
+
#
|
|
54
|
+
# When an Array is given, an octopus merge is performed; a {Git::Branch}
|
|
55
|
+
# object is coerced to a String via `#to_s`.
|
|
56
|
+
#
|
|
57
|
+
# @param message [String, nil] optional commit message for the merge commit
|
|
58
|
+
#
|
|
59
|
+
# Translated to the `-m` flag internally. For fast-forward merges git ignores
|
|
60
|
+
# this value; use `no_ff: true` to ensure a merge commit is created and the
|
|
61
|
+
# message is recorded.
|
|
62
|
+
#
|
|
63
|
+
# @param opts [Hash] additional options forwarded to `git merge`
|
|
64
|
+
#
|
|
65
|
+
# @option opts [Boolean, nil] :no_commit (nil) stop before creating the merge commit
|
|
66
|
+
# (`--no-commit`)
|
|
67
|
+
#
|
|
68
|
+
# @option opts [Boolean, nil] :no_ff (nil) create a merge commit even when
|
|
69
|
+
# fast-forward is possible (`--no-ff`)
|
|
70
|
+
#
|
|
71
|
+
# @option opts [String] :message (nil) commit message
|
|
72
|
+
#
|
|
73
|
+
# Prefer the `:m` option instead of this one. Translated to the `-m` flag.
|
|
74
|
+
# Identical to the positional `message` argument and the `:m` option.
|
|
75
|
+
#
|
|
76
|
+
# @option opts [String] :m (nil) commit message (`-m` flag)
|
|
77
|
+
#
|
|
78
|
+
# @return [String] git's stdout from the merge command
|
|
79
|
+
#
|
|
80
|
+
# @raise [ArgumentError] when unsupported options are provided
|
|
81
|
+
#
|
|
82
|
+
# @raise [Git::FailedError] when git exits with a non-zero exit status
|
|
83
|
+
#
|
|
84
|
+
def merge(branch, message = nil, opts = {})
|
|
85
|
+
SharedPrivate.assert_valid_opts!(MERGE_ALLOWED_OPTS, **opts)
|
|
86
|
+
|
|
87
|
+
# Dup so callers who reuse the same opts hash are not affected
|
|
88
|
+
opts = opts.dup
|
|
89
|
+
|
|
90
|
+
# Merge positional message into opts so the rest of the logic is uniform
|
|
91
|
+
opts[:message] = message if message
|
|
92
|
+
|
|
93
|
+
# git merge uses -m, not --message; translate the key
|
|
94
|
+
opts[:m] = opts.delete(:message) if opts.key?(:message)
|
|
95
|
+
|
|
96
|
+
branches = Array(branch).map(&:to_s)
|
|
97
|
+
Git::Commands::Merge::Start.new(@execution_context).call(*branches, no_edit: true, **opts).stdout
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Find common ancestor commit(s) for use in a merge
|
|
101
|
+
#
|
|
102
|
+
# @example Find the common ancestor of two branches
|
|
103
|
+
# repo.merge_base('main', 'feature') #=> ["abc123def456..."]
|
|
104
|
+
#
|
|
105
|
+
# @example Find all common ancestors of two branches
|
|
106
|
+
# repo.merge_base('branch-a', 'branch-b', all: true)
|
|
107
|
+
#
|
|
108
|
+
# @example Find the fork point of a branch (consults the reflog)
|
|
109
|
+
# repo.merge_base('main', 'feature', fork_point: true)
|
|
110
|
+
#
|
|
111
|
+
# @example Find independent commits not reachable from each other
|
|
112
|
+
# repo.merge_base('abc1234', 'main', 'feature', independent: true)
|
|
113
|
+
#
|
|
114
|
+
# @overload merge_base(*commits, options = {})
|
|
115
|
+
#
|
|
116
|
+
# @param commits [Array<String>] two or more commit SHAs, branch names,
|
|
117
|
+
# or refs to find the common ancestor(s) of
|
|
118
|
+
#
|
|
119
|
+
# @param options [Hash] merge-base options
|
|
120
|
+
#
|
|
121
|
+
# @option options [Boolean, nil] :octopus (nil) compute the best common
|
|
122
|
+
# ancestor for an n-way merge (intersection of all merge bases)
|
|
123
|
+
#
|
|
124
|
+
# @option options [Boolean, nil] :independent (nil) list commits not
|
|
125
|
+
# reachable from any other; useful for finding minimal merge points
|
|
126
|
+
#
|
|
127
|
+
# @option options [Boolean, nil] :fork_point (nil) find the fork point
|
|
128
|
+
# where a branch diverged from another, consulting the reflog
|
|
129
|
+
#
|
|
130
|
+
# @option options [Boolean, nil] :all (nil) output all merge bases instead
|
|
131
|
+
# of just the first when multiple equally good bases exist
|
|
132
|
+
#
|
|
133
|
+
# @return [Array<String>] commit SHAs of the common ancestor(s); empty
|
|
134
|
+
# when no common ancestor exists or `--fork-point` finds none
|
|
135
|
+
#
|
|
136
|
+
# @raise [ArgumentError] when unsupported options are provided
|
|
137
|
+
#
|
|
138
|
+
# @raise [Git::FailedError] when `git merge-base` exits outside the
|
|
139
|
+
# allowed range (exit code > 1)
|
|
140
|
+
#
|
|
141
|
+
def merge_base(*args)
|
|
142
|
+
opts = args.last.is_a?(Hash) ? args.pop : {}
|
|
143
|
+
SharedPrivate.assert_valid_opts!(MERGE_BASE_ALLOWED_OPTS, **opts)
|
|
144
|
+
result = Git::Commands::MergeBase.new(@execution_context).call(*args, **opts)
|
|
145
|
+
result.stdout.lines.map(&:strip).reject(&:empty?)
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# Iterate over files with merge conflicts, yielding conflict details for each
|
|
149
|
+
#
|
|
150
|
+
# For each unmerged file, the staged content for both sides of the conflict
|
|
151
|
+
# (stage 2 "ours" and stage 3 "theirs") is written to temporary files whose
|
|
152
|
+
# paths are yielded alongside the file path. The temporary files are deleted
|
|
153
|
+
# automatically when the block returns.
|
|
154
|
+
#
|
|
155
|
+
# @example Inspect conflicting files
|
|
156
|
+
# repo.each_conflict do |file, your_version, their_version|
|
|
157
|
+
# puts "Conflict in #{file}"
|
|
158
|
+
# puts "Your version:"
|
|
159
|
+
# puts File.read(your_version)
|
|
160
|
+
# puts "Their version:"
|
|
161
|
+
# puts File.read(their_version)
|
|
162
|
+
# end
|
|
163
|
+
#
|
|
164
|
+
# @return [Array<String>] the list of unmerged file paths
|
|
165
|
+
#
|
|
166
|
+
# @raise [Git::FailedError] when `git diff --cached` exits with a non-zero status
|
|
167
|
+
#
|
|
168
|
+
# @yield [file, your_version, their_version] passes conflict details for
|
|
169
|
+
# each unmerged file
|
|
170
|
+
#
|
|
171
|
+
# @yieldparam file [String] path to the conflicting file, relative to the
|
|
172
|
+
# working tree
|
|
173
|
+
#
|
|
174
|
+
# @yieldparam your_version [String] path to a temporary file containing the
|
|
175
|
+
# stage-2 (ours) content for the conflicting file
|
|
176
|
+
#
|
|
177
|
+
# @yieldparam their_version [String] path to a temporary file containing the
|
|
178
|
+
# stage-3 (theirs) content for the conflicting file
|
|
179
|
+
#
|
|
180
|
+
# @yieldreturn [void]
|
|
181
|
+
#
|
|
182
|
+
def each_conflict
|
|
183
|
+
Private.unmerged_paths(@execution_context).each do |file_path|
|
|
184
|
+
Private.write_staged_file(@execution_context, file_path, 2) do |your_file|
|
|
185
|
+
Private.write_staged_file(@execution_context, file_path, 3) do |their_file|
|
|
186
|
+
yield(file_path, your_file.path, their_file.path)
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
# Option keys accepted by {#revert}
|
|
193
|
+
#
|
|
194
|
+
# Derived from the 4.x option map for `Git::Lib#revert`.
|
|
195
|
+
REVERT_ALLOWED_OPTS = %i[no_edit].freeze
|
|
196
|
+
private_constant :REVERT_ALLOWED_OPTS
|
|
197
|
+
|
|
198
|
+
# Revert one or more existing commits by creating new commits that undo
|
|
199
|
+
# the changes those commits introduced
|
|
200
|
+
#
|
|
201
|
+
# The working tree must be clean before calling this method. By default
|
|
202
|
+
# the editor is suppressed (`--no-edit`) so the commit message is taken
|
|
203
|
+
# from git's default revert message without prompting.
|
|
204
|
+
#
|
|
205
|
+
# @example Revert the most recent commit
|
|
206
|
+
# repo.revert('HEAD')
|
|
207
|
+
#
|
|
208
|
+
# @example Revert a specific commit by SHA
|
|
209
|
+
# repo.revert('abc1234')
|
|
210
|
+
#
|
|
211
|
+
# @example Revert a range of commits
|
|
212
|
+
# repo.revert('HEAD~3..HEAD~1')
|
|
213
|
+
#
|
|
214
|
+
# @example Revert without suppressing the editor
|
|
215
|
+
# repo.revert('HEAD', no_edit: false)
|
|
216
|
+
#
|
|
217
|
+
# @param commitish [String, nil] the commit, ref, or rev range to revert;
|
|
218
|
+
# see `gitrevisions(7)` for accepted forms; defaults to `'HEAD'` when
|
|
219
|
+
# `nil`
|
|
220
|
+
#
|
|
221
|
+
# @param opts [Hash] additional options forwarded to `git revert`
|
|
222
|
+
#
|
|
223
|
+
# @option opts [Boolean, nil] :no_edit (true) suppress the commit-message
|
|
224
|
+
# editor (`--no-edit`); pass `false` to open the editor
|
|
225
|
+
#
|
|
226
|
+
# @return [String] git's stdout from the revert command
|
|
227
|
+
#
|
|
228
|
+
# @raise [ArgumentError] when unsupported options are provided
|
|
229
|
+
#
|
|
230
|
+
# @raise [Git::FailedError] when git exits with a non-zero exit status
|
|
231
|
+
#
|
|
232
|
+
def revert(commitish = nil, opts = {})
|
|
233
|
+
commitish = 'HEAD' if commitish.nil?
|
|
234
|
+
SharedPrivate.assert_valid_opts!(REVERT_ALLOWED_OPTS, **opts)
|
|
235
|
+
opts = { no_edit: true }.merge(opts)
|
|
236
|
+
Git::Commands::Revert::Start.new(@execution_context).call(commitish, **opts).stdout
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
# Private helpers local to {Git::Repository::Merging}
|
|
240
|
+
#
|
|
241
|
+
# @api private
|
|
242
|
+
module Private
|
|
243
|
+
# Tempfile name prefixes for staged content, keyed by git stage index
|
|
244
|
+
STAGE_PREFIXES = { 2 => 'YOUR-', 3 => 'THEIR-' }.freeze
|
|
245
|
+
|
|
246
|
+
module_function
|
|
247
|
+
|
|
248
|
+
# Returns the list of file paths with unresolved merge conflicts
|
|
249
|
+
#
|
|
250
|
+
# @param execution_context [Git::ExecutionContext] the execution context
|
|
251
|
+
# used to run git commands
|
|
252
|
+
#
|
|
253
|
+
# @return [Array<String>] unmerged file paths
|
|
254
|
+
#
|
|
255
|
+
# @api private
|
|
256
|
+
#
|
|
257
|
+
def unmerged_paths(execution_context)
|
|
258
|
+
result = Git::Commands::Diff.new(execution_context).call(cached: true)
|
|
259
|
+
result.stdout.split("\n").filter_map do |line|
|
|
260
|
+
::Regexp.last_match(1) if line =~ /^\* Unmerged path (.*)/
|
|
261
|
+
end
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
# Creates a Tempfile with the staged content for `file_path` at `stage`
|
|
265
|
+
# and yields the open IO object to the block
|
|
266
|
+
#
|
|
267
|
+
# @param execution_context [Git::ExecutionContext] the execution context
|
|
268
|
+
# used to run git commands
|
|
269
|
+
#
|
|
270
|
+
# @param file_path [String] repository-relative path to the conflicting file
|
|
271
|
+
#
|
|
272
|
+
# @param stage [Integer] git stage index (2 = ours, 3 = theirs)
|
|
273
|
+
#
|
|
274
|
+
# @yield [f] yields the open Tempfile containing the staged content
|
|
275
|
+
#
|
|
276
|
+
# @yieldparam f [Tempfile] open IO object for the staged content
|
|
277
|
+
#
|
|
278
|
+
# @yieldreturn [void]
|
|
279
|
+
#
|
|
280
|
+
# @return [void]
|
|
281
|
+
#
|
|
282
|
+
# @api private
|
|
283
|
+
#
|
|
284
|
+
def write_staged_file(execution_context, file_path, stage)
|
|
285
|
+
Tempfile.create([STAGE_PREFIXES[stage], File.basename(file_path)]) do |f|
|
|
286
|
+
Git::Commands::Show.new(execution_context).call(":#{stage}:#{file_path}", out: f)
|
|
287
|
+
f.flush
|
|
288
|
+
yield f
|
|
289
|
+
end
|
|
290
|
+
end
|
|
291
|
+
end
|
|
292
|
+
private_constant :Private
|
|
293
|
+
end
|
|
294
|
+
end
|
|
295
|
+
end
|