git 5.0.0.beta.1 → 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 +6 -0
- data/.github/prompts/iteratively-address-copilot-reviews.prompt.md +188 -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 +28 -14
- data/.github/skills/facade-implementation/SKILL.md +14 -0
- data/.github/skills/facade-test-conventions/SKILL.md +14 -0
- data/.rubocop.yml +5 -0
- data/README.md +51 -11
- data/UPGRADING.md +141 -0
- data/git.gemspec +5 -0
- data/lib/git/branch.rb +7 -18
- data/lib/git/branches.rb +2 -10
- data/lib/git/command_line/base.rb +10 -0
- data/lib/git/command_line/capturing.rb +5 -3
- data/lib/git/command_line/streaming.rb +5 -3
- data/lib/git/command_line.rb +3 -3
- data/lib/git/commands/base.rb +7 -6
- data/lib/git/commands/cat_file/batch.rb +6 -1
- data/lib/git/commands/cat_file/raw.rb +7 -1
- data/lib/git/commands/config_option_syntax/get_urlmatch.rb +5 -0
- data/lib/git/commands/show_ref/exclude_existing.rb +1 -1
- data/lib/git/commands/update_ref/batch.rb +1 -1
- data/lib/git/commands/version.rb +5 -0
- data/lib/git/commands.rb +5 -7
- 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/diff.rb +4 -8
- data/lib/git/diff_path_status.rb +2 -13
- data/lib/git/diff_stats.rb +1 -9
- data/lib/git/execution_context/global.rb +3 -28
- data/lib/git/execution_context/repository.rb +30 -41
- data/lib/git/execution_context.rb +43 -24
- data/lib/git/log.rb +3 -9
- data/lib/git/object.rb +14 -21
- data/lib/git/parsers/config_entry.rb +110 -0
- data/lib/git/parsers/ls_remote.rb +79 -0
- data/lib/git/remote.rb +7 -20
- data/lib/git/repository/branching.rb +183 -12
- data/lib/git/repository/committing.rb +64 -68
- data/lib/git/repository/configuring.rb +208 -13
- data/lib/git/repository/context_helpers.rb +264 -0
- data/lib/git/repository/factories.rb +682 -0
- data/lib/git/repository/inspecting.rb +99 -0
- data/lib/git/repository/maintenance.rb +65 -0
- data/lib/git/repository/merging.rb +63 -1
- data/lib/git/repository/object_operations.rb +133 -35
- data/lib/git/repository/path_resolver.rb +1 -1
- data/lib/git/repository/remote_operations.rb +166 -21
- data/lib/git/repository/staging.rb +187 -23
- data/lib/git/repository/stashing.rb +39 -3
- data/lib/git/repository/status_operations.rb +21 -0
- data/lib/git/repository.rb +68 -129
- data/lib/git/stash.rb +2 -9
- data/lib/git/stashes.rb +2 -7
- data/lib/git/status.rb +8 -17
- data/lib/git/version.rb +2 -2
- data/lib/git/worktree.rb +2 -15
- data/lib/git/worktrees.rb +2 -15
- data/lib/git.rb +180 -77
- data/redesign/3_architecture_implementation.md +148 -111
- 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
- metadata +19 -5
- data/lib/git/base.rb +0 -1204
- data/lib/git/lib.rb +0 -2855
|
@@ -2,9 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
require 'git/commands/config_option_syntax'
|
|
4
4
|
require 'git/commands/fetch'
|
|
5
|
+
require 'git/commands/ls_remote'
|
|
5
6
|
require 'git/commands/pull'
|
|
6
7
|
require 'git/commands/push'
|
|
7
8
|
require 'git/commands/remote'
|
|
9
|
+
require 'git/parsers/ls_remote'
|
|
8
10
|
require 'git/remote'
|
|
9
11
|
|
|
10
12
|
require 'git/repository/shared_private'
|
|
@@ -17,7 +19,7 @@ module Git
|
|
|
17
19
|
#
|
|
18
20
|
# @api public
|
|
19
21
|
#
|
|
20
|
-
module RemoteOperations
|
|
22
|
+
module RemoteOperations # rubocop:disable Metrics/ModuleLength
|
|
21
23
|
# Key normalizations for {#fetch} options
|
|
22
24
|
#
|
|
23
25
|
# Maps dash-style option keys (which the 4.x `Git::Lib#fetch` accepted)
|
|
@@ -370,11 +372,11 @@ module Git
|
|
|
370
372
|
Private.push_tags(@execution_context, remote, opts).stdout
|
|
371
373
|
end
|
|
372
374
|
|
|
373
|
-
# Option keys accepted by {#
|
|
375
|
+
# Option keys accepted by {#remote_add}
|
|
374
376
|
#
|
|
375
377
|
# Derived from the 4.x `REMOTE_ADD_OPTION_MAP` in `Git::Lib`.
|
|
376
|
-
|
|
377
|
-
private_constant :
|
|
378
|
+
REMOTE_ADD_ALLOWED_OPTS = %i[fetch track].freeze
|
|
379
|
+
private_constant :REMOTE_ADD_ALLOWED_OPTS
|
|
378
380
|
|
|
379
381
|
# Register a new remote in the local repository
|
|
380
382
|
#
|
|
@@ -382,19 +384,19 @@ module Git
|
|
|
382
384
|
# configures which branches are tracked.
|
|
383
385
|
#
|
|
384
386
|
# @example Add a remote
|
|
385
|
-
# repo.
|
|
387
|
+
# repo.remote_add('upstream', 'https://github.com/user/repo.git')
|
|
386
388
|
#
|
|
387
389
|
# @example Add a remote and fetch immediately
|
|
388
|
-
# repo.
|
|
390
|
+
# repo.remote_add('upstream', 'https://github.com/user/repo.git', fetch: true)
|
|
389
391
|
#
|
|
390
392
|
# @example Add a remote tracking a specific branch
|
|
391
|
-
# repo.
|
|
393
|
+
# repo.remote_add('upstream', 'https://github.com/user/repo.git', track: 'main')
|
|
392
394
|
#
|
|
393
395
|
# @param name [String] the name for the new remote
|
|
394
396
|
#
|
|
395
|
-
# @param url [String, Git::
|
|
397
|
+
# @param url [String, Git::Repository] the URL of the remote repository
|
|
396
398
|
#
|
|
397
|
-
# A {Git::
|
|
399
|
+
# A {Git::Repository} instance is accepted for local references and converted
|
|
398
400
|
# to `url.repo.to_s`.
|
|
399
401
|
#
|
|
400
402
|
# @param opts [Hash] options for adding the remote
|
|
@@ -414,22 +416,56 @@ module Git
|
|
|
414
416
|
#
|
|
415
417
|
# @raise [Git::FailedError] when git exits with a non-zero status
|
|
416
418
|
#
|
|
417
|
-
def
|
|
418
|
-
url = url.repo.to_s if url.is_a?(Git::
|
|
419
|
+
def remote_add(name, url, opts = {})
|
|
420
|
+
url = url.repo.to_s if url.is_a?(Git::Repository)
|
|
419
421
|
opts = Private.normalize_add_remote_keys(opts)
|
|
420
|
-
SharedPrivate.assert_valid_opts!(
|
|
422
|
+
SharedPrivate.assert_valid_opts!(REMOTE_ADD_ALLOWED_OPTS, **opts)
|
|
421
423
|
Git::Commands::Remote::Add.new(@execution_context).call(name, url, **opts)
|
|
422
424
|
|
|
423
425
|
Git::Remote.new(self, name)
|
|
424
426
|
end
|
|
425
427
|
|
|
428
|
+
# @deprecated Use {#remote_add} instead.
|
|
429
|
+
#
|
|
430
|
+
# @param name [String] the name for the new remote
|
|
431
|
+
#
|
|
432
|
+
# @param url [String, Git::Repository] the URL of the remote repository
|
|
433
|
+
#
|
|
434
|
+
# A {Git::Repository} instance is accepted for local references and converted
|
|
435
|
+
# to `url.repo.to_s`.
|
|
436
|
+
#
|
|
437
|
+
# @param opts [Hash] options for adding the remote
|
|
438
|
+
#
|
|
439
|
+
# @option opts [Boolean, nil] :fetch (nil) fetch from the remote immediately
|
|
440
|
+
# after adding it (`-f`)
|
|
441
|
+
#
|
|
442
|
+
# The deprecated alias `:with_fetch` is accepted and normalized
|
|
443
|
+
# automatically.
|
|
444
|
+
#
|
|
445
|
+
# @option opts [String, nil] :track (nil) track only the given branch during
|
|
446
|
+
# fetch (`-t`)
|
|
447
|
+
#
|
|
448
|
+
# @return [Git::Remote] the newly added remote
|
|
449
|
+
#
|
|
450
|
+
# @raise [ArgumentError] when unsupported option keys are provided
|
|
451
|
+
#
|
|
452
|
+
# @raise [Git::FailedError] when git exits with a non-zero status
|
|
453
|
+
#
|
|
454
|
+
def add_remote(name, url, opts = {})
|
|
455
|
+
Git::Deprecation.warn(
|
|
456
|
+
'Git::Repository#add_remote is deprecated and will be removed in v6.0.0. ' \
|
|
457
|
+
'Use Git::Repository#remote_add instead.'
|
|
458
|
+
)
|
|
459
|
+
remote_add(name, url, opts)
|
|
460
|
+
end
|
|
461
|
+
|
|
426
462
|
# Removes a remote from this repository
|
|
427
463
|
#
|
|
428
464
|
# Deletes the remote named `name` along with its associated configuration,
|
|
429
465
|
# tracking references, and remote-tracking branches.
|
|
430
466
|
#
|
|
431
467
|
# @example Remove a remote named 'upstream'
|
|
432
|
-
# repo.
|
|
468
|
+
# repo.remote_remove('upstream')
|
|
433
469
|
#
|
|
434
470
|
# @param name [String] the name of the remote to remove
|
|
435
471
|
#
|
|
@@ -437,39 +473,76 @@ module Git
|
|
|
437
473
|
#
|
|
438
474
|
# @raise [Git::FailedError] when git exits with a non-zero status
|
|
439
475
|
#
|
|
440
|
-
def
|
|
476
|
+
def remote_remove(name)
|
|
441
477
|
Git::Commands::Remote::Remove.new(@execution_context).call(name)
|
|
442
478
|
end
|
|
443
479
|
|
|
480
|
+
# @deprecated Use {#remote_remove} instead.
|
|
481
|
+
#
|
|
482
|
+
# @param name [String] the name of the remote to remove
|
|
483
|
+
#
|
|
484
|
+
# @return [Git::CommandLineResult] the result of calling `git remote remove`
|
|
485
|
+
#
|
|
486
|
+
# @raise [Git::FailedError] when git exits with a non-zero status
|
|
487
|
+
#
|
|
488
|
+
def remove_remote(name)
|
|
489
|
+
Git::Deprecation.warn(
|
|
490
|
+
'Git::Repository#remove_remote is deprecated and will be removed in v6.0.0. ' \
|
|
491
|
+
'Use Git::Repository#remote_remove instead.'
|
|
492
|
+
)
|
|
493
|
+
remote_remove(name)
|
|
494
|
+
end
|
|
495
|
+
|
|
444
496
|
# Sets the URL for an existing remote
|
|
445
497
|
#
|
|
446
498
|
# Replaces the fetch URL configured for the remote named `name`.
|
|
447
499
|
#
|
|
448
500
|
# @example Set the URL for a remote
|
|
449
|
-
# repo.
|
|
501
|
+
# repo.remote_set_url('origin', 'https://github.com/user/repo.git')
|
|
450
502
|
#
|
|
451
503
|
# @example Set the URL from a local repository reference
|
|
452
504
|
# source = Git.open('/path/to/source')
|
|
453
|
-
# repo.
|
|
505
|
+
# repo.remote_set_url('origin', source)
|
|
454
506
|
#
|
|
455
507
|
# @param name [String] the name of the remote to update
|
|
456
508
|
#
|
|
457
|
-
# @param url [String, Git::
|
|
509
|
+
# @param url [String, Git::Repository] the new URL for the remote
|
|
458
510
|
#
|
|
459
|
-
# A {Git::
|
|
511
|
+
# A {Git::Repository} instance is accepted for local references and converted
|
|
460
512
|
# to `url.repo.to_s`.
|
|
461
513
|
#
|
|
462
514
|
# @return [Git::Remote] the updated remote
|
|
463
515
|
#
|
|
464
516
|
# @raise [Git::FailedError] when git exits with a non-zero status
|
|
465
517
|
#
|
|
466
|
-
def
|
|
467
|
-
url = url.repo.to_s if url.is_a?(Git::
|
|
518
|
+
def remote_set_url(name, url)
|
|
519
|
+
url = url.repo.to_s if url.is_a?(Git::Repository)
|
|
468
520
|
Git::Commands::Remote::SetUrl.new(@execution_context).call(name, url)
|
|
469
521
|
|
|
470
522
|
Git::Remote.new(self, name)
|
|
471
523
|
end
|
|
472
524
|
|
|
525
|
+
# @deprecated Use {#remote_set_url} instead.
|
|
526
|
+
#
|
|
527
|
+
# @param name [String] the name of the remote to update
|
|
528
|
+
#
|
|
529
|
+
# @param url [String, Git::Repository] the new URL for the remote
|
|
530
|
+
#
|
|
531
|
+
# A {Git::Repository} instance is accepted for local references and converted
|
|
532
|
+
# to `url.repo.to_s`.
|
|
533
|
+
#
|
|
534
|
+
# @return [Git::Remote] the updated remote
|
|
535
|
+
#
|
|
536
|
+
# @raise [Git::FailedError] when git exits with a non-zero status
|
|
537
|
+
#
|
|
538
|
+
def set_remote_url(name, url)
|
|
539
|
+
Git::Deprecation.warn(
|
|
540
|
+
'Git::Repository#set_remote_url is deprecated and will be removed in v6.0.0. ' \
|
|
541
|
+
'Use Git::Repository#remote_set_url instead.'
|
|
542
|
+
)
|
|
543
|
+
remote_set_url(name, url)
|
|
544
|
+
end
|
|
545
|
+
|
|
473
546
|
# Configures which branches are fetched for a remote
|
|
474
547
|
#
|
|
475
548
|
# Uses `git remote set-branches` to set or append fetch refspecs. When the
|
|
@@ -570,6 +643,78 @@ module Git
|
|
|
570
643
|
result.stdout.split("\n").map { |name| Git::Remote.new(self, name) }
|
|
571
644
|
end
|
|
572
645
|
|
|
646
|
+
# Option keys accepted by {#ls_remote}
|
|
647
|
+
#
|
|
648
|
+
# @return [Array<Symbol>]
|
|
649
|
+
#
|
|
650
|
+
# @api private
|
|
651
|
+
#
|
|
652
|
+
LS_REMOTE_ALLOWED_OPTS = %i[
|
|
653
|
+
branches b heads h tags t refs upload_pack quiet q exit_code sort server_option o timeout
|
|
654
|
+
].freeze
|
|
655
|
+
private_constant :LS_REMOTE_ALLOWED_OPTS
|
|
656
|
+
|
|
657
|
+
# List references available in a remote repository
|
|
658
|
+
#
|
|
659
|
+
# Queries a remote for its available refs and returns a structured Hash
|
|
660
|
+
# mapping ref types to name/sha pairs. The remote is contacted but no local
|
|
661
|
+
# objects are created or updated.
|
|
662
|
+
#
|
|
663
|
+
# @example List all refs from the local repository
|
|
664
|
+
# repo.ls_remote
|
|
665
|
+
# # => {"head"=>{ref: "HEAD", sha: "abc123"},
|
|
666
|
+
# # "branches"=>{"main"=>{ref: "refs", sha: "abc123"}}}
|
|
667
|
+
#
|
|
668
|
+
# @note The `:ref` value in each pair is only the **first path segment** of the
|
|
669
|
+
# full git ref (e.g. `"refs"` for `refs/heads/main`), not the complete ref
|
|
670
|
+
# path. This matches the behavior of 4.x. See
|
|
671
|
+
# [issue 1416](https://github.com/ruby-git/ruby-git/issues/1416) for the
|
|
672
|
+
# planned fix.
|
|
673
|
+
#
|
|
674
|
+
# @example List all refs from a named remote
|
|
675
|
+
# repo.ls_remote('origin')
|
|
676
|
+
# # => {"head"=>..., "branches"=>..., "tags"=>...}
|
|
677
|
+
#
|
|
678
|
+
# @example List only tags from a named remote
|
|
679
|
+
# repo.ls_remote('origin', tags: true)
|
|
680
|
+
# # => {"tags"=>{"v1.0"=>{ref: "refs", sha: "def456"}}}
|
|
681
|
+
#
|
|
682
|
+
# @param location [String, nil] the remote name or URL to query; defaults to
|
|
683
|
+
# `'.'` (the local repository) when nil
|
|
684
|
+
#
|
|
685
|
+
# @param opts [Hash] options for the ls-remote command
|
|
686
|
+
#
|
|
687
|
+
# @option opts [Boolean, nil] :branches (nil) limit output to refs under
|
|
688
|
+
# `refs/heads/`; alias: `:b`
|
|
689
|
+
#
|
|
690
|
+
# @option opts [Boolean, nil] :heads (nil) limit output to refs under
|
|
691
|
+
# `refs/heads/`; kept for backward compatibility; alias: `:h`
|
|
692
|
+
#
|
|
693
|
+
# @option opts [Boolean, nil] :tags (nil) limit output to refs under
|
|
694
|
+
# `refs/tags/`; alias: `:t`
|
|
695
|
+
#
|
|
696
|
+
# @option opts [Boolean, nil] :refs (nil) exclude peeled tags and pseudorefs
|
|
697
|
+
# like `HEAD` from the output
|
|
698
|
+
#
|
|
699
|
+
# @option opts [Numeric] :timeout (nil) execution timeout in seconds
|
|
700
|
+
#
|
|
701
|
+
# @return [Hash{String => Hash}] a Hash keyed by ref type (e.g. `"head"`,
|
|
702
|
+
# `"branches"`, `"tags"`; other git namespace segments may appear for
|
|
703
|
+
# non-standard refs); for named refs the value is a Hash keyed by ref name
|
|
704
|
+
# mapping to `{ ref: String, sha: String }`; for the `"head"` entry the value
|
|
705
|
+
# is `{ ref: String, sha: String }` directly
|
|
706
|
+
#
|
|
707
|
+
# @raise [ArgumentError] if unsupported options are provided
|
|
708
|
+
#
|
|
709
|
+
# @raise [Git::FailedError] if git exits outside the allowed range (exit code > 2)
|
|
710
|
+
#
|
|
711
|
+
def ls_remote(location = nil, opts = {})
|
|
712
|
+
SharedPrivate.assert_valid_opts!(LS_REMOTE_ALLOWED_OPTS, **opts)
|
|
713
|
+
repository = location || '.'
|
|
714
|
+
output_lines = Git::Commands::LsRemote.new(@execution_context).call(repository, **opts).stdout.split("\n")
|
|
715
|
+
Git::Parsers::LsRemote.parse_output(output_lines)
|
|
716
|
+
end
|
|
717
|
+
|
|
573
718
|
# Helpers private to the `RemoteOperations` topic module
|
|
574
719
|
#
|
|
575
720
|
# @api private
|
|
@@ -707,7 +852,7 @@ module Git
|
|
|
707
852
|
Git::Commands::Push.new(execution_context).call(*[remote].compact, **opts)
|
|
708
853
|
end
|
|
709
854
|
|
|
710
|
-
# Normalize deprecated {#
|
|
855
|
+
# Normalize deprecated option keys for {#remote_add} to their canonical equivalents
|
|
711
856
|
#
|
|
712
857
|
# Renames the deprecated `:with_fetch` key to `:fetch`, removing it from
|
|
713
858
|
# the copy. When both keys are present, `:with_fetch` takes precedence.
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'git/commands/add'
|
|
4
|
+
require 'git/commands/am/apply'
|
|
5
|
+
require 'git/commands/apply'
|
|
4
6
|
require 'git/commands/clean'
|
|
5
7
|
require 'git/commands/ls_files'
|
|
8
|
+
require 'git/commands/mv'
|
|
9
|
+
require 'git/commands/read_tree'
|
|
6
10
|
require 'git/commands/reset'
|
|
7
11
|
require 'git/commands/rm'
|
|
8
12
|
require 'git/escaped_path'
|
|
@@ -10,8 +14,8 @@ require 'git/repository/shared_private'
|
|
|
10
14
|
|
|
11
15
|
module Git
|
|
12
16
|
class Repository
|
|
13
|
-
# Facade methods for staging-area operations: adding, resetting,
|
|
14
|
-
# cleaning files
|
|
17
|
+
# Facade methods for staging-area operations: adding, resetting, moving,
|
|
18
|
+
# removing, and cleaning files
|
|
15
19
|
#
|
|
16
20
|
# Included by {Git::Repository}.
|
|
17
21
|
#
|
|
@@ -63,31 +67,140 @@ module Git
|
|
|
63
67
|
|
|
64
68
|
# Reset the current HEAD to a specified state
|
|
65
69
|
#
|
|
66
|
-
# @
|
|
70
|
+
# @example Reset the index and working tree to HEAD
|
|
71
|
+
# repo.reset
|
|
67
72
|
#
|
|
68
|
-
#
|
|
69
|
-
#
|
|
73
|
+
# @example Hard reset to a specific commit
|
|
74
|
+
# repo.reset('HEAD~1', hard: true)
|
|
75
|
+
#
|
|
76
|
+
# @param commitish [String, nil] the commit or tree-ish to reset to;
|
|
77
|
+
# defaults to HEAD when `nil`
|
|
78
|
+
#
|
|
79
|
+
# @param opts [Hash] options for the reset command
|
|
80
|
+
#
|
|
81
|
+
# @option opts [Boolean, nil] :hard (nil) reset the index and working
|
|
82
|
+
# tree; discards all tracked changes
|
|
83
|
+
#
|
|
84
|
+
# @return [String] git's stdout from the reset
|
|
85
|
+
#
|
|
86
|
+
# @raise [ArgumentError] when unsupported options are provided
|
|
87
|
+
#
|
|
88
|
+
# @raise [Git::FailedError] when git exits with a non-zero exit status
|
|
89
|
+
#
|
|
90
|
+
def reset(commitish = nil, opts = {})
|
|
91
|
+
SharedPrivate.assert_valid_opts!(RESET_ALLOWED_OPTS, **opts)
|
|
92
|
+
Git::Commands::Reset.new(@execution_context).call(commitish, **opts).stdout
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Reset the current HEAD to a specified state with `--hard`
|
|
96
|
+
#
|
|
97
|
+
# @deprecated Use {#reset} with `hard: true` instead.
|
|
98
|
+
#
|
|
99
|
+
# @example Hard reset to HEAD
|
|
100
|
+
# repo.reset_hard
|
|
70
101
|
#
|
|
71
102
|
# @example Hard reset to a specific commit
|
|
72
|
-
# repo.
|
|
103
|
+
# repo.reset_hard('HEAD~1')
|
|
73
104
|
#
|
|
74
|
-
#
|
|
75
|
-
#
|
|
105
|
+
# @param commitish [String, nil] the commit or tree-ish to reset to;
|
|
106
|
+
# defaults to HEAD when `nil`
|
|
76
107
|
#
|
|
77
|
-
#
|
|
108
|
+
# @param opts [Hash] options passed through to {#reset}
|
|
78
109
|
#
|
|
79
|
-
#
|
|
80
|
-
# tree; discards all tracked changes
|
|
110
|
+
# @return [String] git's stdout from the reset
|
|
81
111
|
#
|
|
82
|
-
#
|
|
112
|
+
# @raise [ArgumentError] when unsupported options are provided
|
|
83
113
|
#
|
|
84
|
-
#
|
|
114
|
+
# @raise [Git::FailedError] when git exits with a non-zero exit status
|
|
85
115
|
#
|
|
86
|
-
|
|
116
|
+
def reset_hard(commitish = nil, opts = {})
|
|
117
|
+
Git::Deprecation.warn(
|
|
118
|
+
'Git::Repository::Staging#reset_hard is deprecated and will be removed in a future version. ' \
|
|
119
|
+
'Use #reset(commitish, hard: true) instead.'
|
|
120
|
+
)
|
|
121
|
+
reset(commitish, **opts, hard: true)
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# Apply a patch file to the working tree
|
|
87
125
|
#
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
126
|
+
# Reads the unified diff in `file` and applies it to the working tree via
|
|
127
|
+
# `git apply`. If `file` does not exist, the method returns `nil` without
|
|
128
|
+
# calling git — preserving the 4.x `Git::Base#apply` no-op contract.
|
|
129
|
+
#
|
|
130
|
+
# @example Apply a patch to the working tree
|
|
131
|
+
# repo.apply('fix.patch')
|
|
132
|
+
#
|
|
133
|
+
# @param file [String] path to the patch file to apply
|
|
134
|
+
#
|
|
135
|
+
# @return [String] git's stdout (usually empty on success)
|
|
136
|
+
#
|
|
137
|
+
# @return [nil] when `file` does not exist
|
|
138
|
+
#
|
|
139
|
+
# @raise [Git::FailedError] when git exits with a non-zero exit status
|
|
140
|
+
#
|
|
141
|
+
def apply(file)
|
|
142
|
+
return unless File.exist?(file)
|
|
143
|
+
|
|
144
|
+
Git::Commands::Apply.new(@execution_context).call(file, chdir: @execution_context.git_work_dir).stdout
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
# Apply a series of patches from a mailbox file to the current branch
|
|
148
|
+
#
|
|
149
|
+
# Reads the mbox-format file in `file` and applies the patches via
|
|
150
|
+
# `git am`. If `file` does not exist, the method returns `nil` without
|
|
151
|
+
# calling git — preserving the 4.x `Git::Base#apply_mail` no-op contract.
|
|
152
|
+
#
|
|
153
|
+
# @example Apply patches from a mailbox
|
|
154
|
+
# repo.apply_mail('patches.mbox')
|
|
155
|
+
#
|
|
156
|
+
# @param file [String] path to the mbox patch file to apply
|
|
157
|
+
#
|
|
158
|
+
# @return [String] git's stdout (usually empty on success)
|
|
159
|
+
#
|
|
160
|
+
# @return [nil] when `file` does not exist
|
|
161
|
+
#
|
|
162
|
+
# @raise [Git::FailedError] when git exits with a non-zero exit status
|
|
163
|
+
#
|
|
164
|
+
def apply_mail(file)
|
|
165
|
+
return unless File.exist?(file)
|
|
166
|
+
|
|
167
|
+
Git::Commands::Am::Apply.new(@execution_context).call(file, chdir: @execution_context.git_work_dir).stdout
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# Option keys accepted by {#read_tree}
|
|
171
|
+
READ_TREE_ALLOWED_OPTS = %i[prefix].freeze
|
|
172
|
+
private_constant :READ_TREE_ALLOWED_OPTS
|
|
173
|
+
|
|
174
|
+
# Read tree information into the index
|
|
175
|
+
#
|
|
176
|
+
# Reads the named tree object into the index. This is a low-level plumbing
|
|
177
|
+
# operation used to stage the contents of a tree without updating the
|
|
178
|
+
# working tree. Typically called before {#checkout_index} or as part of
|
|
179
|
+
# custom merge flows.
|
|
180
|
+
#
|
|
181
|
+
# @example Read HEAD into the index
|
|
182
|
+
# repo.read_tree('HEAD')
|
|
183
|
+
#
|
|
184
|
+
# @example Read a tree under a prefix directory
|
|
185
|
+
# repo.read_tree('HEAD', { prefix: 'subdir/' })
|
|
186
|
+
#
|
|
187
|
+
# @param treeish [String] the tree-ish to read into the index
|
|
188
|
+
#
|
|
189
|
+
# @param opts [Hash] options for the read-tree command
|
|
190
|
+
#
|
|
191
|
+
# @option opts [String] :prefix (nil) keep the current index contents and
|
|
192
|
+
# read the named tree-ish under the directory at the given prefix
|
|
193
|
+
# (`--prefix=<prefix>`)
|
|
194
|
+
#
|
|
195
|
+
# @return [String] git's stdout (usually empty on success)
|
|
196
|
+
#
|
|
197
|
+
# @raise [ArgumentError] when unsupported options are provided
|
|
198
|
+
#
|
|
199
|
+
# @raise [Git::FailedError] when git exits with a non-zero exit status
|
|
200
|
+
#
|
|
201
|
+
def read_tree(treeish, opts = {})
|
|
202
|
+
SharedPrivate.assert_valid_opts!(READ_TREE_ALLOWED_OPTS, **opts)
|
|
203
|
+
Git::Commands::ReadTree.new(@execution_context).call(treeish, **opts).stdout
|
|
91
204
|
end
|
|
92
205
|
|
|
93
206
|
# Option keys accepted by {#rm}
|
|
@@ -100,13 +213,13 @@ module Git
|
|
|
100
213
|
# Remove file(s) from the working tree and the index
|
|
101
214
|
#
|
|
102
215
|
# @example Remove a single file
|
|
103
|
-
# repo.rm('obsolete.txt', force: true)
|
|
216
|
+
# repo.rm('obsolete.txt', { force: true })
|
|
104
217
|
#
|
|
105
218
|
# @example Remove a directory recursively
|
|
106
|
-
# repo.rm('build', r: true)
|
|
219
|
+
# repo.rm('build', { r: true })
|
|
107
220
|
#
|
|
108
221
|
# @example Remove from the index only, keeping the working tree copy
|
|
109
|
-
# repo.rm('keep_me.txt', cached: true)
|
|
222
|
+
# repo.rm('keep_me.txt', { cached: true })
|
|
110
223
|
#
|
|
111
224
|
# @param path [String, Array<String>] a file or files to remove (relative to
|
|
112
225
|
# the worktree root); defaults to `'.'` (all files)
|
|
@@ -157,6 +270,57 @@ module Git
|
|
|
157
270
|
Git::Commands::Rm.new(@execution_context).call(*Array(path), **opts).stdout
|
|
158
271
|
end
|
|
159
272
|
|
|
273
|
+
alias remove rm
|
|
274
|
+
|
|
275
|
+
# Option keys accepted by {#mv}
|
|
276
|
+
MV_ALLOWED_OPTS = %i[force f dry_run n k].freeze
|
|
277
|
+
private_constant :MV_ALLOWED_OPTS
|
|
278
|
+
|
|
279
|
+
# Move or rename a file, directory, or symlink in the working tree
|
|
280
|
+
#
|
|
281
|
+
# Updates the index after successful completion, but the change must still
|
|
282
|
+
# be committed.
|
|
283
|
+
#
|
|
284
|
+
# @example Move a single file
|
|
285
|
+
# repo.mv('old.rb', 'new.rb')
|
|
286
|
+
#
|
|
287
|
+
# @example Move multiple files to a directory
|
|
288
|
+
# repo.mv(['file1.rb', 'file2.rb'], 'destination_dir/')
|
|
289
|
+
#
|
|
290
|
+
# @example Force overwrite if destination exists
|
|
291
|
+
# repo.mv('source.rb', 'dest.rb', force: true)
|
|
292
|
+
#
|
|
293
|
+
# @param source [String, Array<String>] one or more source file(s),
|
|
294
|
+
# directory(ies), or symlink(s) to move (relative to the worktree root)
|
|
295
|
+
#
|
|
296
|
+
# @param destination [String] the destination file or directory
|
|
297
|
+
#
|
|
298
|
+
# @param options [Hash] options for the mv command
|
|
299
|
+
#
|
|
300
|
+
# @option options [Boolean, nil] :force (nil) force renaming or moving even
|
|
301
|
+
# if the destination exists (alias: `:f`)
|
|
302
|
+
#
|
|
303
|
+
# @option options [Boolean, nil] :f (nil) alias for `:force`
|
|
304
|
+
#
|
|
305
|
+
# @option options [Boolean, nil] :dry_run (nil) do not actually move any
|
|
306
|
+
# files; only show what would happen (alias: `:n`)
|
|
307
|
+
#
|
|
308
|
+
# @option options [Boolean, nil] :n (nil) alias for `:dry_run`
|
|
309
|
+
#
|
|
310
|
+
# @option options [Boolean, nil] :k (nil) skip move or rename actions which
|
|
311
|
+
# would lead to an error
|
|
312
|
+
#
|
|
313
|
+
# @return [String] git's stdout from the mv command
|
|
314
|
+
#
|
|
315
|
+
# @raise [ArgumentError] when unsupported options are provided
|
|
316
|
+
#
|
|
317
|
+
# @raise [Git::FailedError] when git exits with a non-zero exit status
|
|
318
|
+
#
|
|
319
|
+
def mv(source, destination, options = {})
|
|
320
|
+
SharedPrivate.assert_valid_opts!(MV_ALLOWED_OPTS, **options)
|
|
321
|
+
Git::Commands::Mv.new(@execution_context).call(*Array(source), destination, verbose: true, **options).stdout
|
|
322
|
+
end
|
|
323
|
+
|
|
160
324
|
# Option keys accepted by {#clean}
|
|
161
325
|
#
|
|
162
326
|
# The deprecated `:ff` and `:force_force` keys are handled by
|
|
@@ -168,13 +332,13 @@ module Git
|
|
|
168
332
|
# Remove untracked files from the working tree
|
|
169
333
|
#
|
|
170
334
|
# @example Remove untracked files
|
|
171
|
-
# repo.clean(force: true)
|
|
335
|
+
# repo.clean({ force: true })
|
|
172
336
|
#
|
|
173
337
|
# @example Remove untracked files and directories
|
|
174
|
-
# repo.clean(force: true, d: true)
|
|
338
|
+
# repo.clean({ force: true, d: true })
|
|
175
339
|
#
|
|
176
340
|
# @example Remove untracked and ignored files
|
|
177
|
-
# repo.clean(force: true, x: true)
|
|
341
|
+
# repo.clean({ force: true, x: true })
|
|
178
342
|
#
|
|
179
343
|
# @param opts [Hash] options for the clean command
|
|
180
344
|
#
|
|
@@ -19,6 +19,13 @@ module Git
|
|
|
19
19
|
# message is the stash description with the leading branch prefix (e.g.
|
|
20
20
|
# `"On main:"` or `"WIP on main:"`) stripped.
|
|
21
21
|
#
|
|
22
|
+
# @note The sequential index returned here is **not** the same as git's
|
|
23
|
+
# `stash@{N}` reference used by {#stash_apply}. In git, `stash@{0}` is the
|
|
24
|
+
# **most recent** stash, while index `0` here is the **oldest**. To apply a
|
|
25
|
+
# specific stash from this list, convert the entry's position to a git
|
|
26
|
+
# reference: `'stash@{%d}' % (total - 1 - index)`, or pass the string
|
|
27
|
+
# reference directly to {#stash_apply}.
|
|
28
|
+
#
|
|
22
29
|
# @example List all stashes (oldest first)
|
|
23
30
|
# repo.stashes_all #=> [[0, "Fix bug"], [1, "Add feature"]]
|
|
24
31
|
#
|
|
@@ -34,12 +41,38 @@ module Git
|
|
|
34
41
|
result = Git::Commands::Stash::List.new(@execution_context).call
|
|
35
42
|
stashes = Git::Parsers::Stash.parse_list(result.stdout)
|
|
36
43
|
stashes.reverse.each_with_index.map do |info, i|
|
|
37
|
-
|
|
38
|
-
message = match_data ? match_data[1].strip : info.message
|
|
44
|
+
message = info.message.sub(/^(?:WIP on|On)\s+[^:]+:\s*/, '')
|
|
39
45
|
[i, message]
|
|
40
46
|
end
|
|
41
47
|
end
|
|
42
48
|
|
|
49
|
+
# Returns stash entries as a formatted string matching `git stash list` output
|
|
50
|
+
#
|
|
51
|
+
# @deprecated Use {#stashes_all} instead.
|
|
52
|
+
#
|
|
53
|
+
# @example List stashes as a formatted string
|
|
54
|
+
# repo.stash_list #=> "stash@{0}: On main: WIP\nstash@{1}: On feature: Fix bug"
|
|
55
|
+
#
|
|
56
|
+
# @return [String] newline-joined `"stash@{n}: <full message>"` entries, or an
|
|
57
|
+
# empty string when there are no stashes; the format matches `git stash list`
|
|
58
|
+
# output
|
|
59
|
+
#
|
|
60
|
+
# @raise [Git::FailedError] if git exits with a non-zero exit status
|
|
61
|
+
#
|
|
62
|
+
# @see #stashes_all
|
|
63
|
+
#
|
|
64
|
+
# @see https://git-scm.com/docs/git-stash git-stash documentation
|
|
65
|
+
#
|
|
66
|
+
def stash_list
|
|
67
|
+
Git::Deprecation.warn(
|
|
68
|
+
'Git::Repository#stash_list is deprecated and will be removed in a future version. ' \
|
|
69
|
+
'Use Git::Repository#stashes_all instead.'
|
|
70
|
+
)
|
|
71
|
+
result = Git::Commands::Stash::List.new(@execution_context).call
|
|
72
|
+
stashes = Git::Parsers::Stash.parse_list(result.stdout)
|
|
73
|
+
stashes.map { |info| "#{info.name}: #{info.message}" }.join("\n")
|
|
74
|
+
end
|
|
75
|
+
|
|
43
76
|
# Save the current working directory and index state to a new stash
|
|
44
77
|
#
|
|
45
78
|
# @param message [String] the stash message
|
|
@@ -72,7 +105,10 @@ module Git
|
|
|
72
105
|
# repo.stash_apply('stash@{1}') #=> "HEAD is now at abc1234 Initial commit"
|
|
73
106
|
#
|
|
74
107
|
# @param id [String, Integer, nil] the stash identifier (e.g., `'stash@{0}'`,
|
|
75
|
-
# `0`) or `nil` to apply the most recent stash entry
|
|
108
|
+
# `0`) or `nil` to apply the most recent stash entry. When an Integer is
|
|
109
|
+
# given it is passed directly to git as `stash@{N}`, where `0` is the
|
|
110
|
+
# **most recent** stash — the opposite order from {#stashes_all}'s
|
|
111
|
+
# sequential indices, where `0` is the **oldest** stash.
|
|
76
112
|
#
|
|
77
113
|
# @return [String] the output from the git stash apply command
|
|
78
114
|
#
|
|
@@ -43,6 +43,27 @@ module Git
|
|
|
43
43
|
true
|
|
44
44
|
end
|
|
45
45
|
|
|
46
|
+
# Returns `true` if the repository has no commits yet
|
|
47
|
+
#
|
|
48
|
+
# @example Check whether a repository is empty
|
|
49
|
+
# repo.empty? #=> true # freshly initialized, no commits yet
|
|
50
|
+
# repo.empty? #=> false # at least one commit exists
|
|
51
|
+
#
|
|
52
|
+
# @return [Boolean] `true` when the repository has no commits, `false` otherwise
|
|
53
|
+
#
|
|
54
|
+
# @raise [Git::FailedError] if git exits with a non-zero exit status other
|
|
55
|
+
# than when the repository has no commits
|
|
56
|
+
#
|
|
57
|
+
# @deprecated Use {#no_commits?} instead
|
|
58
|
+
#
|
|
59
|
+
def empty?
|
|
60
|
+
Git::Deprecation.warn(
|
|
61
|
+
'Git::Repository#empty? is deprecated and will be removed in a future version. ' \
|
|
62
|
+
'Use Git::Repository#no_commits? instead.'
|
|
63
|
+
)
|
|
64
|
+
no_commits?
|
|
65
|
+
end
|
|
66
|
+
|
|
46
67
|
# List all files in the working tree that are not tracked by git
|
|
47
68
|
#
|
|
48
69
|
# Runs `git ls-files --others --exclude-standard` from the working tree
|