git 1.19.1 → 4.4.5
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/.commitlintrc.yml +38 -0
- data/.github/copilot-instructions.md +2733 -0
- data/.github/pull_request_template.md +17 -0
- data/.github/workflows/continuous_integration.yml +92 -21
- data/.github/workflows/enforce_conventional_commits.yml +29 -0
- data/.github/workflows/experimental_continuous_integration.yml +59 -0
- data/.github/workflows/release.yml +53 -0
- data/.gitignore +5 -0
- data/.husky/commit-msg +1 -0
- data/.release-please-manifest.json +3 -0
- data/.rubocop.yml +55 -0
- data/.rubocop_todo.yml +12 -0
- data/.yardopts +4 -1
- data/AI_POLICY.md +24 -0
- data/CHANGELOG.md +501 -0
- data/CODE_OF_CONDUCT.md +25 -0
- data/CONTRIBUTING.md +323 -102
- data/GOVERNANCE.md +106 -0
- data/LICENSE +1 -1
- data/MAINTAINERS.md +17 -4
- data/README.md +575 -246
- data/Rakefile +13 -55
- data/git.gemspec +36 -30
- data/lib/git/args_builder.rb +111 -0
- data/lib/git/author.rb +9 -7
- data/lib/git/base.rb +602 -173
- data/lib/git/branch.rb +318 -38
- data/lib/git/branches.rb +21 -24
- data/lib/git/command_line.rb +330 -0
- data/lib/git/command_line_result.rb +9 -3
- data/lib/git/config.rb +10 -6
- data/lib/git/diff.rb +149 -81
- data/lib/git/diff_path_status.rb +46 -0
- data/lib/git/diff_stats.rb +59 -0
- data/lib/git/errors.rb +212 -0
- data/lib/git/escaped_path.rb +2 -2
- data/lib/git/fsck_object.rb +48 -0
- data/lib/git/fsck_result.rb +121 -0
- data/lib/git/index.rb +2 -1
- data/lib/git/lib.rb +1648 -643
- data/lib/git/log.rb +143 -106
- data/lib/git/object.rb +151 -125
- data/lib/git/path.rb +23 -16
- data/lib/git/remote.rb +5 -4
- data/lib/git/repository.rb +2 -2
- data/lib/git/stash.rb +11 -12
- data/lib/git/stashes.rb +16 -15
- data/lib/git/status.rb +104 -143
- data/lib/git/url.rb +3 -3
- data/lib/git/version.rb +3 -1
- data/lib/git/working_directory.rb +2 -0
- data/lib/git/worktree.rb +6 -5
- data/lib/git/worktrees.rb +6 -6
- data/lib/git.rb +131 -28
- data/package.json +10 -0
- data/redesign/1_architecture_existing.md +66 -0
- data/redesign/2_architecture_redesign.md +130 -0
- data/redesign/3_architecture_implementation.md +138 -0
- data/redesign/index.md +34 -0
- data/release-please-config.json +36 -0
- data/tasks/gem_tasks.rake +10 -0
- data/tasks/rubocop.rake +12 -0
- data/tasks/test.rake +13 -0
- data/tasks/test_gem.rake +12 -0
- data/tasks/yard.rake +23 -0
- metadata +114 -37
- data/.github/stale.yml +0 -25
- data/Dockerfile.changelog-rs +0 -12
- data/PULL_REQUEST_TEMPLATE.md +0 -9
- data/RELEASING.md +0 -70
- data/lib/git/base/factory.rb +0 -99
- data/lib/git/failed_error.rb +0 -53
- data/lib/git/git_execute_error.rb +0 -7
- data/lib/git/signaled_error.rb +0 -50
- /data/{ISSUE_TEMPLATE.md → .github/issue_template.md} +0 -0
data/README.md
CHANGED
|
@@ -7,156 +7,219 @@
|
|
|
7
7
|
|
|
8
8
|
[](https://badge.fury.io/rb/git)
|
|
9
9
|
[](https://rubydoc.info/gems/git/)
|
|
10
|
-
[](https://rubydoc.info/gems/git/file/CHANGELOG.md)
|
|
12
|
+
[](https://github.com/ruby-git/ruby-git/actions?query=workflow%3ACI)
|
|
14
|
+
[](https://conventionalcommits.org)
|
|
16
|
+
[](AI_POLICY.md)
|
|
17
|
+
|
|
18
|
+
- [Summary](#summary)
|
|
19
|
+
- [Install](#install)
|
|
20
|
+
- [Quick Start](#quick-start)
|
|
21
|
+
- [Examples](#examples)
|
|
22
|
+
- [Configuration](#configuration)
|
|
23
|
+
- [Read Operations](#read-operations)
|
|
24
|
+
- [Write Operations](#write-operations)
|
|
25
|
+
- [Index and Tree Operations](#index-and-tree-operations)
|
|
26
|
+
- [Errors Raised By This Gem](#errors-raised-by-this-gem)
|
|
27
|
+
- [Specifying And Handling Timeouts](#specifying-and-handling-timeouts)
|
|
28
|
+
- [Deprecations](#deprecations)
|
|
29
|
+
- [Project Policies](#project-policies)
|
|
30
|
+
- [Ruby Version Support Policy](#ruby-version-support-policy)
|
|
31
|
+
- [Git Version Support Policy](#git-version-support-policy)
|
|
32
|
+
- [📢 Project Announcements 📢](#-project-announcements-)
|
|
33
|
+
- [2026-01-07: AI Policy Introduced](#2026-01-07-ai-policy-introduced)
|
|
34
|
+
- [2025-07-09: Architectural Redesign](#2025-07-09-architectural-redesign)
|
|
35
|
+
- [2025-07-07: We Now Use RuboCop](#2025-07-07-we-now-use-rubocop)
|
|
36
|
+
- [2025-06-06: Default Branch Rename](#2025-06-06-default-branch-rename)
|
|
37
|
+
- [2025-05-15: We've Switched to Conventional Commits](#2025-05-15-weve-switched-to-conventional-commits)
|
|
38
|
+
|
|
39
|
+
## Summary
|
|
40
|
+
|
|
41
|
+
The [git gem](https://rubygems.org/gems/git) provides a Ruby interface to the `git`
|
|
42
|
+
command line.
|
|
13
43
|
|
|
14
|
-
|
|
15
|
-
create, read, and manipulate Git repositories by wrapping system calls to the `git`
|
|
16
|
-
command line. The API can be used for working with Git in complex interactions
|
|
17
|
-
including branching and merging, object inspection and manipulation, history, patch
|
|
18
|
-
generation and more.
|
|
19
|
-
|
|
20
|
-
## v2.0.0 pre-release
|
|
21
|
-
|
|
22
|
-
git 2.0.0 is available as a pre-release version for testing! Please give it a try.
|
|
44
|
+
Get started by obtaining a repository object by:
|
|
23
45
|
|
|
24
|
-
|
|
25
|
-
|
|
46
|
+
- opening an existing working copy with
|
|
47
|
+
[Git.open](https://rubydoc.info/gems/git/Git#open-class_method)
|
|
48
|
+
- initializing a new repository with
|
|
49
|
+
[Git.init](https://rubydoc.info/gems/git/Git#init-class_method)
|
|
50
|
+
- cloning a repository with
|
|
51
|
+
[Git.clone](https://rubydoc.info/gems/git/Git#clone-class_method)
|
|
26
52
|
|
|
27
|
-
|
|
53
|
+
Methods that can be called on a repository object are documented in
|
|
54
|
+
[Git::Base](https://rubydoc.info/gems/git/Git/Base)
|
|
28
55
|
|
|
29
|
-
|
|
30
|
-
* Create a policy of supported Git CLI versions (released 2020-12-25)
|
|
31
|
-
* Update the required Ruby version to at least 3.0 (released 2020-07-27)
|
|
32
|
-
* Update the required Git command line version to at least 2.28
|
|
33
|
-
* Update how CLI commands are called to use the [process_executer](https://github.com/main-branch/process_executer)
|
|
34
|
-
gem which is built on top of [Kernel.spawn](https://ruby-doc.org/3.3.0/Kernel.html#method-i-spawn).
|
|
35
|
-
See [PR #617](https://github.com/ruby-git/ruby-git/pull/617) for more details
|
|
36
|
-
on the motivation for this implementation.
|
|
56
|
+
## Install
|
|
37
57
|
|
|
38
|
-
|
|
39
|
-
the feedback received during the pre-release period.
|
|
58
|
+
Install the gem and add to the application's Gemfile by executing:
|
|
40
59
|
|
|
41
|
-
|
|
42
|
-
|
|
60
|
+
```shell
|
|
61
|
+
bundle add git
|
|
62
|
+
```
|
|
43
63
|
|
|
44
|
-
|
|
64
|
+
to install version 1.x:
|
|
45
65
|
|
|
46
|
-
|
|
66
|
+
```shell
|
|
67
|
+
bundle add git --version "~> 1.19"
|
|
68
|
+
```
|
|
47
69
|
|
|
48
|
-
|
|
70
|
+
If bundler is not being used to manage dependencies, install the gem by executing:
|
|
49
71
|
|
|
50
|
-
|
|
72
|
+
```shell
|
|
73
|
+
gem install git
|
|
74
|
+
```
|
|
51
75
|
|
|
52
|
-
|
|
76
|
+
to install version 1.x:
|
|
53
77
|
|
|
54
|
-
|
|
78
|
+
```shell
|
|
79
|
+
gem install git --version "~> 1.19"
|
|
80
|
+
```
|
|
55
81
|
|
|
56
|
-
|
|
82
|
+
## Quick Start
|
|
57
83
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
84
|
+
All functionality for this gem starts with the top-level
|
|
85
|
+
[`Git`](https://rubydoc.info/gems/git/Git) module. This module can be used to run
|
|
86
|
+
non-repo scoped `git` commands such as `config`.
|
|
61
87
|
|
|
62
|
-
|
|
88
|
+
The `Git` module also has factory methods such as `open`, `clone`, and `init` which
|
|
89
|
+
return a [`Git::Base`](https://rubydoc.info/gems/git/Git/Base) object. The
|
|
90
|
+
`Git::Base` object is used to run repo-specific `git` commands such as `add`,
|
|
91
|
+
`commit`, `push`, and `log`.
|
|
63
92
|
|
|
64
|
-
|
|
93
|
+
Clone, read status, and log:
|
|
65
94
|
|
|
66
|
-
|
|
95
|
+
```ruby
|
|
96
|
+
require 'git'
|
|
67
97
|
|
|
68
|
-
|
|
69
|
-
|
|
98
|
+
repo = Git.clone('https://github.com/ruby-git/ruby-git.git', 'ruby-git')
|
|
99
|
+
repo.status.changed.each { |f| puts "changed: #{f.path}" }
|
|
100
|
+
repo.log(5).each { |c| puts c.message }
|
|
70
101
|
```
|
|
71
102
|
|
|
72
|
-
|
|
103
|
+
Open an existing repo and commit:
|
|
73
104
|
|
|
105
|
+
```ruby
|
|
106
|
+
require 'git'
|
|
74
107
|
|
|
75
|
-
|
|
108
|
+
repo = Git.open('/path/to/repo')
|
|
109
|
+
repo.add(all: true)
|
|
110
|
+
repo.commit('chore: update files')
|
|
111
|
+
repo.push
|
|
112
|
+
```
|
|
76
113
|
|
|
77
|
-
|
|
114
|
+
Initialize a new repo and make the first commit:
|
|
78
115
|
|
|
79
|
-
|
|
116
|
+
```ruby
|
|
117
|
+
require 'git'
|
|
80
118
|
|
|
81
|
-
|
|
119
|
+
repo = Git.init('my_project')
|
|
120
|
+
repo.add(all: true)
|
|
121
|
+
repo.commit('initial commit')
|
|
122
|
+
```
|
|
82
123
|
|
|
83
|
-
|
|
84
|
-
`Git:Status::StatusFile` objects for each object in git, which includes files in the working
|
|
85
|
-
directory, in the index and in the repository. Similar to running 'git status' on the command line to determine untracked and changed files.
|
|
124
|
+
## Examples
|
|
86
125
|
|
|
87
|
-
|
|
126
|
+
Beyond the basics covered in Quick Start, these examples show the full range of
|
|
127
|
+
options and variations for each operation.
|
|
88
128
|
|
|
89
|
-
|
|
129
|
+
### Configuration
|
|
90
130
|
|
|
91
|
-
|
|
92
|
-
like:
|
|
131
|
+
Configure the `git` command line:
|
|
93
132
|
|
|
94
|
-
|
|
133
|
+
```ruby
|
|
134
|
+
# Global config (in ~/.gitconfig)
|
|
135
|
+
settings = Git.global_config # returns a Hash
|
|
136
|
+
username = Git.global_config('user.email')
|
|
137
|
+
Git.global_config('user.email', 'user@example.com')
|
|
138
|
+
|
|
139
|
+
# Repository config
|
|
140
|
+
repo = Git.open('path/to/repo')
|
|
141
|
+
settings = repo.config # returns a Hash
|
|
142
|
+
username = repo.config('user.email')
|
|
143
|
+
repo.config('user.email', 'anotheruser@example.com')
|
|
144
|
+
```
|
|
95
145
|
|
|
96
|
-
|
|
146
|
+
Configure the git gem:
|
|
97
147
|
|
|
98
|
-
|
|
148
|
+
```ruby
|
|
149
|
+
Git.configure do |config|
|
|
150
|
+
config.binary_path = '/usr/local/bin/git'
|
|
151
|
+
config.git_ssh = 'ssh -i ~/.ssh/id_rsa'
|
|
152
|
+
end
|
|
99
153
|
|
|
100
|
-
|
|
154
|
+
# or
|
|
101
155
|
|
|
102
|
-
|
|
156
|
+
Git.config.binary_path = '/usr/local/bin/git'
|
|
157
|
+
Git.config.git_ssh = 'ssh -i ~/.ssh/id_rsa'
|
|
158
|
+
```
|
|
103
159
|
|
|
104
|
-
|
|
160
|
+
**How SSH configuration is determined:**
|
|
105
161
|
|
|
106
|
-
|
|
162
|
+
- If `git_ssh` is not specified in the API call, the global config (`Git.configure {
|
|
163
|
+
|c| c.git_ssh = ... }`) is used.
|
|
164
|
+
- If `git_ssh: nil` is specified, SSH is disabled for that instance (no SSH key or
|
|
165
|
+
script will be used).
|
|
166
|
+
- If `git_ssh` is a non-empty string, it is used for that instance (overriding the
|
|
167
|
+
global config).
|
|
107
168
|
|
|
108
|
-
|
|
109
|
-
require 'rubygems'
|
|
110
|
-
```
|
|
169
|
+
You can also specify a custom SSH script on a per-repository basis:
|
|
111
170
|
|
|
112
|
-
Require the 'git' gem.
|
|
113
171
|
```ruby
|
|
114
|
-
|
|
115
|
-
|
|
172
|
+
# Use a specific SSH key for a single repository
|
|
173
|
+
git = Git.open('/path/to/repo', git_ssh: 'ssh -i /path/to/private_key')
|
|
116
174
|
|
|
117
|
-
|
|
175
|
+
# Or when cloning
|
|
176
|
+
git = Git.clone('git@github.com:user/repo.git', 'local-dir',
|
|
177
|
+
git_ssh: 'ssh -i /path/to/private_key')
|
|
118
178
|
|
|
119
|
-
|
|
120
|
-
Git.
|
|
121
|
-
# If you want to use a custom git binary
|
|
122
|
-
config.binary_path = '/git/bin/path'
|
|
123
|
-
|
|
124
|
-
# If you need to use a custom SSH script
|
|
125
|
-
config.git_ssh = '/path/to/ssh/script'
|
|
126
|
-
end
|
|
179
|
+
# Or when initializing
|
|
180
|
+
git = Git.init('new-repo', git_ssh: 'ssh -i /path/to/private_key')
|
|
127
181
|
```
|
|
128
182
|
|
|
129
|
-
|
|
183
|
+
This is especially useful in multi-threaded applications where different repositories
|
|
184
|
+
require different SSH credentials.
|
|
130
185
|
|
|
131
|
-
|
|
186
|
+
### Read Operations
|
|
187
|
+
|
|
188
|
+
Here are the operations that need read permission only:
|
|
132
189
|
|
|
133
190
|
```ruby
|
|
134
|
-
|
|
191
|
+
repo = Git.open(working_dir, :log => Logger.new(STDOUT))
|
|
192
|
+
|
|
193
|
+
repo.index
|
|
194
|
+
repo.index.readable?
|
|
195
|
+
repo.index.writable?
|
|
196
|
+
repo.repo
|
|
197
|
+
repo.dir
|
|
135
198
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
g.index.writable?
|
|
139
|
-
g.repo
|
|
140
|
-
g.dir
|
|
199
|
+
# ls-tree with recursion into subtrees (list files)
|
|
200
|
+
repo.ls_tree("HEAD", recursive: true)
|
|
141
201
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
202
|
+
# log - returns a Git::Log object, which is an Enumerator of Git::Commit objects
|
|
203
|
+
# default configuration returns a max of 30 commits
|
|
204
|
+
repo.log
|
|
205
|
+
repo.log(200) # 200 most recent commits
|
|
206
|
+
repo.log.since('2 weeks ago') # default count of commits since 2 weeks ago.
|
|
207
|
+
repo.log(200).since('2 weeks ago') # commits since 2 weeks ago, limited to 200.
|
|
208
|
+
repo.log.between('v2.5', 'v2.6')
|
|
209
|
+
repo.log.each {|l| puts l.sha }
|
|
210
|
+
repo.gblob('v2.5:Makefile').log.since('2 weeks ago')
|
|
148
211
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
212
|
+
repo.object('HEAD^').to_s # git show / git rev-parse
|
|
213
|
+
repo.object('HEAD^').contents
|
|
214
|
+
repo.object('v2.5:Makefile').size
|
|
215
|
+
repo.object('v2.5:Makefile').sha
|
|
153
216
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
217
|
+
repo.gtree(treeish)
|
|
218
|
+
repo.gblob(treeish)
|
|
219
|
+
repo.gcommit(treeish)
|
|
157
220
|
|
|
158
221
|
|
|
159
|
-
commit =
|
|
222
|
+
commit = repo.gcommit('1cc8667014381')
|
|
160
223
|
|
|
161
224
|
commit.gtree
|
|
162
225
|
commit.parent.sha
|
|
@@ -168,59 +231,74 @@ commit.committer.name
|
|
|
168
231
|
commit.date.strftime("%m-%d-%y")
|
|
169
232
|
commit.message
|
|
170
233
|
|
|
171
|
-
tree =
|
|
234
|
+
tree = repo.gtree("HEAD^{tree}")
|
|
172
235
|
|
|
173
236
|
tree.blobs
|
|
174
237
|
tree.subtrees
|
|
175
238
|
tree.children # blobs and subtrees
|
|
176
239
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
240
|
+
repo.rev_parse('v2.0.0:README.md')
|
|
241
|
+
|
|
242
|
+
repo.branches # returns Git::Branch objects
|
|
243
|
+
repo.branches.local
|
|
244
|
+
repo.current_branch
|
|
245
|
+
repo.branches.remote
|
|
246
|
+
repo.branches[:main].gcommit
|
|
247
|
+
repo.branches['origin/main'].gcommit
|
|
248
|
+
|
|
249
|
+
repo.grep('hello') # implies HEAD
|
|
250
|
+
repo.blob('v2.5:Makefile').grep('hello')
|
|
251
|
+
repo.tag('v2.5').grep('hello', 'docs/')
|
|
252
|
+
repo.describe()
|
|
253
|
+
repo.describe('0djf2aa')
|
|
254
|
+
repo.describe('HEAD', {:all => true, :tags => true})
|
|
255
|
+
|
|
256
|
+
repo.diff(commit1, commit2).size
|
|
257
|
+
repo.diff(commit1, commit2).stats
|
|
258
|
+
repo.diff(commit1, commit2).name_status
|
|
259
|
+
repo.gtree('v2.5').diff('v2.6').insertions
|
|
260
|
+
repo.diff('gitsearch1', 'v2.5').path('lib/')
|
|
261
|
+
repo.diff('gitsearch1', 'v2.5').path('lib/', 'docs/', 'README.md') # multiple paths
|
|
262
|
+
repo.diff('gitsearch1', repo.gtree('v2.5'))
|
|
263
|
+
repo.diff('gitsearch1', 'v2.5').path('docs/').patch
|
|
264
|
+
repo.gtree('v2.5').diff('v2.6').patch
|
|
265
|
+
|
|
266
|
+
repo.gtree('v2.5').diff('v2.6').each do |file_diff|
|
|
203
267
|
puts file_diff.path
|
|
204
268
|
puts file_diff.patch
|
|
205
269
|
puts file_diff.blob(:src).contents
|
|
206
270
|
end
|
|
207
271
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
272
|
+
repo.worktrees # returns Git::Worktree objects
|
|
273
|
+
repo.worktrees.count
|
|
274
|
+
repo.worktrees.each do |worktree|
|
|
211
275
|
worktree.dir
|
|
212
276
|
worktree.gcommit
|
|
213
277
|
worktree.to_s
|
|
214
278
|
end
|
|
215
279
|
|
|
216
|
-
|
|
217
|
-
|
|
280
|
+
# Check repository integrity with fsck
|
|
281
|
+
result = repo.fsck
|
|
282
|
+
result.dangling.each { |obj| puts "dangling #{obj.type}: #{obj.sha}" }
|
|
283
|
+
result.missing.each { |obj| puts "missing #{obj.type}: #{obj.sha}" }
|
|
284
|
+
|
|
285
|
+
# Check if repository has any issues
|
|
286
|
+
puts "Repository is clean" if result.empty?
|
|
287
|
+
|
|
288
|
+
# fsck with options
|
|
289
|
+
result = repo.fsck(unreachable: true, strict: true)
|
|
290
|
+
|
|
291
|
+
# Suppress dangling object output
|
|
292
|
+
result = repo.fsck(dangling: false)
|
|
293
|
+
|
|
294
|
+
repo.config('user.name') # returns 'Scott Chacon'
|
|
295
|
+
repo.config # returns whole config hash
|
|
218
296
|
|
|
219
297
|
# Configuration can be set when cloning using the :config option.
|
|
220
298
|
# This option can be an single configuration String or an Array
|
|
221
299
|
# if multiple config items need to be set.
|
|
222
300
|
#
|
|
223
|
-
|
|
301
|
+
repo = Git.clone(
|
|
224
302
|
git_uri, destination_path,
|
|
225
303
|
:config => [
|
|
226
304
|
'core.sshCommand=ssh -i /home/user/.ssh/id_rsa',
|
|
@@ -228,200 +306,451 @@ g = Git.clone(
|
|
|
228
306
|
]
|
|
229
307
|
)
|
|
230
308
|
|
|
231
|
-
|
|
309
|
+
repo.tags # returns array of Git::Tag objects
|
|
232
310
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
311
|
+
repo.show()
|
|
312
|
+
repo.show('HEAD')
|
|
313
|
+
repo.show('v2.8', 'README.md')
|
|
236
314
|
|
|
237
315
|
Git.ls_remote('https://github.com/ruby-git/ruby-git.git') # returns a hash containing the available references of the repo.
|
|
238
316
|
Git.ls_remote('/path/to/local/repo')
|
|
239
317
|
Git.ls_remote() # same as Git.ls_remote('.')
|
|
240
318
|
|
|
241
|
-
Git.default_branch('https://github.com/ruby-git/ruby-git') #=> '
|
|
319
|
+
Git.default_branch('https://github.com/ruby-git/ruby-git') #=> 'main'
|
|
242
320
|
```
|
|
243
321
|
|
|
322
|
+
### Write Operations
|
|
323
|
+
|
|
244
324
|
And here are the operations that will need to write to your git repository.
|
|
245
325
|
|
|
246
326
|
```ruby
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
327
|
+
repo = Git.init # default is the current directory
|
|
328
|
+
repo = Git.init('project')
|
|
329
|
+
repo = Git.init(
|
|
330
|
+
'/home/schacon/proj',
|
|
331
|
+
{ :repository => '/opt/git/proj.git', :index => '/tmp/index'}
|
|
332
|
+
)
|
|
252
333
|
|
|
253
334
|
# Clone from a git url
|
|
254
335
|
git_url = 'https://github.com/ruby-git/ruby-git.git'
|
|
255
|
-
|
|
256
|
-
g = Git.clone(git_url)
|
|
336
|
+
repo = Git.clone(git_url)
|
|
257
337
|
|
|
258
338
|
# Clone into /tmp/clone/ruby-git-clean
|
|
259
339
|
name = 'ruby-git-clean'
|
|
260
340
|
path = '/tmp/clone'
|
|
261
|
-
|
|
262
|
-
|
|
341
|
+
repo = Git.clone(git_url, name, :path => path)
|
|
342
|
+
repo.dir #=> /tmp/clone/ruby-git-clean
|
|
263
343
|
|
|
264
|
-
|
|
265
|
-
|
|
344
|
+
repo.config('user.name', 'Scott Chacon')
|
|
345
|
+
repo.config('user.email', 'email@email.com')
|
|
266
346
|
|
|
267
347
|
# Clone can take a filter to tell the serve to send a partial clone
|
|
268
|
-
|
|
348
|
+
repo = Git.clone(git_url, name, :path => path, :filter => 'tree:0')
|
|
349
|
+
|
|
350
|
+
# Clone can control single-branch behavior (nil default keeps current git behavior)
|
|
351
|
+
repo = Git.clone(git_url, name, :path => path, :depth => 1, :single_branch => false)
|
|
269
352
|
|
|
270
353
|
# Clone can take an optional logger
|
|
271
|
-
logger = Logger.new
|
|
272
|
-
|
|
354
|
+
logger = Logger.new(STDOUT)
|
|
355
|
+
repo = Git.clone(git_url, 'my-repo', :log => logger)
|
|
273
356
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
357
|
+
repo.add # git add -- "."
|
|
358
|
+
repo.add(:all=>true) # git add --all -- "."
|
|
359
|
+
repo.add('file_path') # git add -- "file_path"
|
|
360
|
+
repo.add(['file_path_1', 'file_path_2']) # git add -- "file_path_1" "file_path_2"
|
|
278
361
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
362
|
+
repo.remove() # git rm -f -- "."
|
|
363
|
+
repo.remove('file.txt') # git rm -f -- "file.txt"
|
|
364
|
+
repo.remove(['file.txt', 'file2.txt']) # git rm -f -- "file.txt" "file2.txt"
|
|
365
|
+
repo.remove('file.txt', :recursive => true) # git rm -f -r -- "file.txt"
|
|
366
|
+
repo.remove('file.txt', :cached => true) # git rm -f --cached -- "file.txt"
|
|
284
367
|
|
|
285
|
-
|
|
286
|
-
|
|
368
|
+
repo.commit('message')
|
|
369
|
+
repo.commit_all('message')
|
|
287
370
|
|
|
288
371
|
# Sign a commit using the gpg key configured in the user.signingkey config setting
|
|
289
|
-
|
|
290
|
-
|
|
372
|
+
repo.config('user.signingkey', '0A46826A')
|
|
373
|
+
repo.commit('message', gpg_sign: true)
|
|
291
374
|
|
|
292
375
|
# Sign a commit using a specified gpg key
|
|
293
376
|
key_id = '0A46826A'
|
|
294
|
-
|
|
377
|
+
repo.commit('message', gpg_sign: key_id)
|
|
295
378
|
|
|
296
379
|
# Skip signing a commit (overriding any global gpgsign setting)
|
|
297
|
-
|
|
380
|
+
repo.commit('message', no_gpg_sign: true)
|
|
298
381
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
end
|
|
382
|
+
repo = Git.clone(git_url, 'myrepo')
|
|
383
|
+
repo.chdir do
|
|
384
|
+
File.write('test-file', 'blahblahblah')
|
|
385
|
+
repo.status.changed.each do |file|
|
|
386
|
+
puts file.blob(:index).contents
|
|
387
|
+
end
|
|
305
388
|
end
|
|
306
389
|
|
|
307
|
-
|
|
308
|
-
|
|
390
|
+
repo.reset # defaults to HEAD
|
|
391
|
+
repo.reset_hard(Git::Commit)
|
|
309
392
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
393
|
+
repo.branch('new_branch') # creates new or fetches existing
|
|
394
|
+
repo.branch('new_branch').checkout
|
|
395
|
+
repo.branch('new_branch').delete
|
|
396
|
+
repo.branch('existing_branch').checkout
|
|
397
|
+
repo.branch('main').contains?('existing_branch')
|
|
315
398
|
|
|
316
399
|
# delete remote branch
|
|
317
|
-
|
|
400
|
+
repo.push('origin', 'remote_branch_name', force: true, delete: true)
|
|
318
401
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
402
|
+
repo.checkout('new_branch')
|
|
403
|
+
repo.checkout('new_branch', new_branch: true, start_point: 'main')
|
|
404
|
+
repo.checkout(repo.branch('new_branch'))
|
|
322
405
|
|
|
323
|
-
|
|
324
|
-
|
|
406
|
+
repo.branch(name).merge(branch2)
|
|
407
|
+
repo.branch(branch2).merge # merges HEAD with branch2
|
|
325
408
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
409
|
+
repo.branch(name).in_branch(message) { # add files } # auto-commits
|
|
410
|
+
repo.merge('new_branch')
|
|
411
|
+
repo.merge('new_branch', 'merge commit message', no_ff: true)
|
|
412
|
+
repo.merge('origin/remote_branch')
|
|
413
|
+
repo.merge(repo.branch('main'))
|
|
414
|
+
repo.merge([branch1, branch2])
|
|
332
415
|
|
|
333
|
-
|
|
416
|
+
repo.merge_base('branch1', 'branch2')
|
|
334
417
|
|
|
335
|
-
r =
|
|
336
|
-
r =
|
|
418
|
+
r = repo.add_remote(name, uri) # Git::Remote
|
|
419
|
+
r = repo.add_remote(name, Git::Base) # Git::Remote
|
|
337
420
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
421
|
+
repo.remotes # array of Git::Remotes
|
|
422
|
+
repo.remote(name).fetch
|
|
423
|
+
repo.remote(name).remove
|
|
424
|
+
repo.remote(name).merge
|
|
425
|
+
repo.remote(name).merge(branch)
|
|
343
426
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
g.fetch('origin', {:ref => 'some/ref/head'} )
|
|
347
|
-
g.fetch(all: true, force: true, depth: 2)
|
|
348
|
-
g.fetch('origin', {:'update-head-ok' => true})
|
|
427
|
+
repo.remote_set_branches('origin', '*', add: true) # append additional fetch refspecs
|
|
428
|
+
repo.remote_set_branches('origin', 'feature', 'release/*') # replace fetch refspecs
|
|
349
429
|
|
|
350
|
-
|
|
351
|
-
|
|
430
|
+
repo.fetch
|
|
431
|
+
repo.fetch(repo.remotes.first)
|
|
432
|
+
repo.fetch('origin', {:ref => 'some/ref/head'} )
|
|
433
|
+
repo.fetch(all: true, force: true, depth: 2)
|
|
434
|
+
repo.fetch('origin', {:'update-head-ok' => true})
|
|
352
435
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
g.add_tag('tag_name', 'object_reference', {:options => 'here'})
|
|
356
|
-
g.add_tag('tag_name', {:options => 'here'})
|
|
436
|
+
repo.pull
|
|
437
|
+
repo.pull(Git::Repo, Git::Branch) # fetch and a merge
|
|
357
438
|
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
:m | :message
|
|
363
|
-
:s
|
|
439
|
+
repo.add_tag('tag_name') # returns Git::Tag
|
|
440
|
+
repo.add_tag('tag_name', 'object_reference')
|
|
441
|
+
repo.add_tag('tag_name', 'object_reference', {:options => 'here'})
|
|
442
|
+
repo.add_tag('tag_name', {:options => 'here'})
|
|
364
443
|
|
|
365
|
-
|
|
444
|
+
repo.delete_tag('tag_name')
|
|
366
445
|
|
|
367
|
-
|
|
446
|
+
repo.repack
|
|
368
447
|
|
|
369
|
-
|
|
370
|
-
|
|
448
|
+
repo.push
|
|
449
|
+
repo.push(repo.remote('name'))
|
|
371
450
|
|
|
372
451
|
# delete remote branch
|
|
373
|
-
|
|
452
|
+
repo.push('origin', 'remote_branch_name', force: true, delete: true)
|
|
374
453
|
|
|
375
454
|
# push all branches to remote at one time
|
|
376
|
-
|
|
455
|
+
repo.push('origin', all: true)
|
|
377
456
|
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
457
|
+
repo.worktree('/tmp/new_worktree').add
|
|
458
|
+
repo.worktree('/tmp/new_worktree', 'branch1').add
|
|
459
|
+
repo.worktree('/tmp/new_worktree').remove
|
|
460
|
+
repo.worktrees.prune
|
|
382
461
|
```
|
|
383
462
|
|
|
463
|
+
### Index and Tree Operations
|
|
464
|
+
|
|
384
465
|
Some examples of more low-level index and tree operations
|
|
385
466
|
|
|
386
467
|
```ruby
|
|
387
|
-
|
|
468
|
+
repo.with_temp_index do
|
|
388
469
|
|
|
389
|
-
|
|
390
|
-
|
|
470
|
+
repo.read_tree(tree3) # calls self.index.read_tree
|
|
471
|
+
repo.read_tree(tree1, :prefix => 'hi/')
|
|
391
472
|
|
|
392
|
-
c =
|
|
473
|
+
c = repo.commit_tree('message')
|
|
393
474
|
# or #
|
|
394
|
-
t =
|
|
395
|
-
c =
|
|
475
|
+
t = repo.write_tree
|
|
476
|
+
c = repo.commit_tree(t, :message => 'message', :parents => [sha1, sha2])
|
|
396
477
|
|
|
397
|
-
|
|
398
|
-
|
|
478
|
+
repo.branch('branch_name').update_ref(c)
|
|
479
|
+
repo.update_ref(branch, c)
|
|
399
480
|
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
481
|
+
repo.with_temp_working do # new blank working directory
|
|
482
|
+
repo.checkout
|
|
483
|
+
repo.checkout(another_index)
|
|
484
|
+
repo.commit # commits to temp_index
|
|
404
485
|
end
|
|
405
486
|
end
|
|
406
487
|
|
|
407
|
-
|
|
488
|
+
repo.set_index('/path/to/index')
|
|
408
489
|
|
|
409
|
-
|
|
410
|
-
g.with_index(path) do
|
|
490
|
+
repo.with_index(path) do
|
|
411
491
|
# calls set_index, then switches back after
|
|
412
492
|
end
|
|
413
493
|
|
|
414
|
-
|
|
494
|
+
repo.with_working(dir) do
|
|
415
495
|
# calls set_working, then switches back after
|
|
416
496
|
end
|
|
417
497
|
|
|
418
|
-
|
|
419
|
-
|
|
498
|
+
repo.with_temp_working(dir) do
|
|
499
|
+
repo.checkout_index(:prefix => dir, :path_limiter => path)
|
|
420
500
|
# do file work
|
|
421
|
-
|
|
501
|
+
repo.commit # commits to index
|
|
502
|
+
end
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
## Errors Raised By This Gem
|
|
506
|
+
|
|
507
|
+
The git gem will only raise an `ArgumentError` or an error that is a subclass of
|
|
508
|
+
`Git::Error`. It does not explicitly raise any other types of errors.
|
|
509
|
+
|
|
510
|
+
It is recommended to rescue `Git::Error` to catch any runtime error raised by this
|
|
511
|
+
gem unless you need more specific error handling.
|
|
512
|
+
|
|
513
|
+
```ruby
|
|
514
|
+
begin
|
|
515
|
+
# some git operation
|
|
516
|
+
rescue Git::Error => e
|
|
517
|
+
puts "An error occurred: #{e.message}"
|
|
422
518
|
end
|
|
423
519
|
```
|
|
424
520
|
|
|
425
|
-
|
|
521
|
+
See [`Git::Error`](https://rubydoc.info/gems/git/Git/Error) for more information.
|
|
522
|
+
|
|
523
|
+
## Specifying And Handling Timeouts
|
|
524
|
+
|
|
525
|
+
The timeout feature was added in git gem version `2.0.0`.
|
|
526
|
+
|
|
527
|
+
A timeout for git command line operations can be set either globally or for specific
|
|
528
|
+
method calls that accept a `:timeout` parameter.
|
|
529
|
+
|
|
530
|
+
The timeout value must be a real, non-negative `Numeric` value that specifies a
|
|
531
|
+
number of seconds a `git` command will be given to complete before being sent a KILL
|
|
532
|
+
signal. This library may hang if the `git` command does not terminate after receiving
|
|
533
|
+
the KILL signal.
|
|
534
|
+
|
|
535
|
+
When a command times out, it is killed by sending it the `SIGKILL` signal and a
|
|
536
|
+
`Git::TimeoutError` is raised. This error derives from the `Git::SignaledError` and
|
|
537
|
+
`Git::Error`.
|
|
538
|
+
|
|
539
|
+
If the timeout value is `0` or `nil`, no timeout will be enforced.
|
|
540
|
+
|
|
541
|
+
If a method accepts a `:timeout` parameter and a receives a non-nil value, the value
|
|
542
|
+
of this parameter will override the global timeout value. In this context, a value of
|
|
543
|
+
`nil` (which is usually the default) will use the global timeout value and a value of
|
|
544
|
+
`0` will turn off timeout enforcement for that method call no matter what the global
|
|
545
|
+
value is.
|
|
546
|
+
|
|
547
|
+
To set a global timeout, use the `Git.config` object:
|
|
548
|
+
|
|
549
|
+
```ruby
|
|
550
|
+
Git.config.timeout = nil # a value of nil or 0 means no timeout is enforced
|
|
551
|
+
Git.config.timeout = 1.5 # can be any real, non-negative Numeric interpreted as number of seconds
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
The global timeout can be overridden for a specific method if the method accepts a
|
|
555
|
+
`:timeout` parameter:
|
|
556
|
+
|
|
557
|
+
```ruby
|
|
558
|
+
repo_url = 'https://github.com/ruby-git/ruby-git.git'
|
|
559
|
+
Git.clone(repo_url) # Use the global timeout value
|
|
560
|
+
Git.clone(repo_url, timeout: nil) # Also uses the global timeout value
|
|
561
|
+
Git.clone(repo_url, timeout: 0) # Do not enforce a timeout
|
|
562
|
+
Git.clone(repo_url, timeout: 10.5) # Timeout after 10.5 seconds raising Git::SignaledError
|
|
563
|
+
```
|
|
564
|
+
|
|
565
|
+
If the command takes too long, a `Git::TimeoutError` will be raised:
|
|
566
|
+
|
|
567
|
+
```ruby
|
|
568
|
+
begin
|
|
569
|
+
Git.clone(repo_url, timeout: 10)
|
|
570
|
+
rescue Git::TimeoutError => e
|
|
571
|
+
e.result.tap do |r|
|
|
572
|
+
r.class #=> Git::CommandLineResult
|
|
573
|
+
r.status #=> #<Process::Status: pid 62173 SIGKILL (signal 9)>
|
|
574
|
+
r.status.timeout? #=> true
|
|
575
|
+
r.git_cmd # The git command ran as an array of strings
|
|
576
|
+
r.stdout # The command's output to stdout until it was terminated
|
|
577
|
+
r.stderr # The command's output to stderr until it was terminated
|
|
578
|
+
end
|
|
579
|
+
end
|
|
580
|
+
```
|
|
581
|
+
|
|
582
|
+
## Deprecations
|
|
583
|
+
|
|
584
|
+
This gem uses ActiveSupport's deprecation mechanism to report deprecation warnings.
|
|
585
|
+
|
|
586
|
+
You can silence deprecation warnings by adding this line to your source code:
|
|
587
|
+
|
|
588
|
+
```ruby
|
|
589
|
+
Git::Deprecation.behavior = :silence
|
|
590
|
+
```
|
|
591
|
+
|
|
592
|
+
Or by setting this environment variable before loading the gem:
|
|
593
|
+
|
|
594
|
+
```sh
|
|
595
|
+
GIT_DEPRECATION_BEHAVIOR=silence
|
|
596
|
+
```
|
|
597
|
+
|
|
598
|
+
Accepted environment variable values are the behavior names supported by your
|
|
599
|
+
installed ActiveSupport version.
|
|
600
|
+
|
|
601
|
+
If `GIT_DEPRECATION_BEHAVIOR` is set to an unsupported value, loading the gem
|
|
602
|
+
raises `ArgumentError` with the accepted behavior names.
|
|
603
|
+
|
|
604
|
+
See [the Active Support Deprecation
|
|
605
|
+
documentation](https://api.rubyonrails.org/classes/ActiveSupport/Deprecation.html)
|
|
606
|
+
for more details.
|
|
607
|
+
|
|
608
|
+
If deprecation warnings are silenced, you should reenable them before upgrading the
|
|
609
|
+
git gem to the next major version. This will make it easier to identify changes
|
|
610
|
+
needed for the upgrade.
|
|
611
|
+
|
|
612
|
+
## Project Policies
|
|
613
|
+
|
|
614
|
+
These documents set expectations for behavior, contribution workflows, AI-assisted
|
|
615
|
+
changes, decision making, maintainer roles, and licensing. Please review them before
|
|
616
|
+
opening issues or pull requests.
|
|
617
|
+
|
|
618
|
+
| Document | Description |
|
|
619
|
+
| -------- | ----------- |
|
|
620
|
+
| [CODE_OF_CONDUCT](CODE_OF_CONDUCT.md) | We follow the Ruby community Code of Conduct; expect respectful, harassment-free participation and report concerns to maintainers. |
|
|
621
|
+
| [CONTRIBUTING](CONTRIBUTING.md) | How to report issues, submit PRs with Conventional Commits, meet coding/testing standards, and follow the Code of Conduct. |
|
|
622
|
+
| [AI_POLICY](AI_POLICY.md) | AI-assisted contributions are welcome. Contributors are expected to read and apply the AI Policy, and ensure any AI-assisted work meets our quality, security, and licensing standards. |
|
|
623
|
+
| [Ruby version support policy](#ruby-version-support-policy) | Supported Ruby runtimes and platforms; bump decisions and CI coverage expectations. |
|
|
624
|
+
| [Git version support policy](#git-version-support-policy) | Minimum supported git version and how version bumps are communicated and enforced. |
|
|
625
|
+
| [GOVERNANCE](GOVERNANCE.md) | Principles-first governance defining maintainer/project lead roles, least-privilege access, consensus/majority decisions, and nomination/emeritus steps. |
|
|
626
|
+
| [MAINTAINERS](MAINTAINERS.md) | Lists active maintainers (Project Lead noted) and emeritus alumni with links; see governance for role scope. |
|
|
627
|
+
| [LICENSE](LICENSE) | MIT License terms for using, modifying, and redistributing this project. |
|
|
628
|
+
|
|
629
|
+
### Ruby Version Support Policy
|
|
630
|
+
|
|
631
|
+
This gem is expected to function correctly on:
|
|
632
|
+
|
|
633
|
+
- All [non-EOL versions](https://www.ruby-lang.org/en/downloads/branches/) of the MRI
|
|
634
|
+
Ruby on Mac, Linux, and Windows
|
|
635
|
+
- The latest version of JRuby 9.4+ on Linux
|
|
636
|
+
- The latest version of TruffleRuby 24+ on Linux
|
|
637
|
+
|
|
638
|
+
It is this project's intent to support the latest version of JRuby on Windows once
|
|
639
|
+
the [process_executer](https://github.com/main-branch/process_executer) gem properly
|
|
640
|
+
supports subprocess status reporting on JRuby for Windows (see
|
|
641
|
+
[main-branch/process_executer#156](https://github.com/main-branch/process_executer/issues/156)).
|
|
642
|
+
|
|
643
|
+
### Git Version Support Policy
|
|
644
|
+
|
|
645
|
+
This gem requires git version 2.28.0 or greater as specified in the gemspec. This
|
|
646
|
+
requirement reflects:
|
|
647
|
+
|
|
648
|
+
- The minimum git version necessary to support all features provided by this gem
|
|
649
|
+
- A reasonable balance between supporting older systems and leveraging modern git
|
|
650
|
+
capabilities
|
|
651
|
+
- The practical limitations of testing across multiple git versions in CI
|
|
652
|
+
|
|
653
|
+
Git 2.28.0 was released on July 27, 2020. While this gem may work with earlier
|
|
654
|
+
versions of git, compatibility with versions prior to 2.28.0 is not tested or
|
|
655
|
+
guaranteed. Users on older git versions should upgrade to at least 2.28.0.
|
|
656
|
+
|
|
657
|
+
The supported git version may be increased in future major or minor releases of this
|
|
658
|
+
gem as new git features are adopted or as maintaining backward compatibility becomes
|
|
659
|
+
impractical. Such changes will be clearly documented in the CHANGELOG and release
|
|
660
|
+
notes.
|
|
661
|
+
|
|
662
|
+
## 📢 Project Announcements 📢
|
|
663
|
+
|
|
664
|
+
### 2026-01-07: AI Policy Introduced
|
|
665
|
+
|
|
666
|
+
We have adopted a formal [AI Policy](AI_POLICY.md) to clarify expectations for
|
|
667
|
+
AI-assisted contributions. Please review it before opening a PR to ensure your
|
|
668
|
+
changes are fully understood, meet our quality bar, and respect licensing
|
|
669
|
+
requirements.
|
|
670
|
+
|
|
671
|
+
We chose a principles-based policy to respect contributors’ time and expertise. It’s
|
|
672
|
+
quick to read, easy to remember, and avoids unnecessary policy overhead while still
|
|
673
|
+
setting clear expectations.
|
|
674
|
+
|
|
675
|
+
### 2025-07-09: Architectural Redesign
|
|
676
|
+
|
|
677
|
+
The git gem is undergoing a significant architectural redesign for the upcoming
|
|
678
|
+
v5.0.0 release. The current architecture has several design challenges that make it
|
|
679
|
+
difficult to maintain and evolve. This redesign aims to address these issues by
|
|
680
|
+
introducing a clearer, more robust, and more testable structure.
|
|
681
|
+
|
|
682
|
+
We have prepared detailed documents outlining the analysis of the current
|
|
683
|
+
architecture and the proposed changes. We encourage our community and contributors to
|
|
684
|
+
review them:
|
|
685
|
+
|
|
686
|
+
1. [Analysis of the Current Architecture](redesign/1_architecture_existing.md): A
|
|
687
|
+
breakdown of the existing design and its challenges.
|
|
688
|
+
2. [The Proposed Redesign](redesign/2_architecture_redesign.md): An overview of the
|
|
689
|
+
new three-layered architecture.
|
|
690
|
+
3. [Implementation Plan](redesign/3_architecture_implementation.md): The step-by-step
|
|
691
|
+
plan for implementing the redesign.
|
|
692
|
+
|
|
693
|
+
Your feedback is welcome! Please feel free to open an issue to discuss the proposed
|
|
694
|
+
changes.
|
|
695
|
+
|
|
696
|
+
> **DON'T PANIC!**
|
|
697
|
+
>
|
|
698
|
+
> While this is a major internal refactoring, our goal is to keep the primary public
|
|
699
|
+
API on the main repository object as stable as possible. Most users who rely on
|
|
700
|
+
documented methods like `g.commit`, `g.add`, and `g.status` should find the
|
|
701
|
+
transition to v5.0.0 straightforward.
|
|
702
|
+
>
|
|
703
|
+
> The breaking changes will primarily affect users who have been relying on the
|
|
704
|
+
internal g.lib accessor, which will be removed as part of this cleanup. For more
|
|
705
|
+
details, please see the "Impact on Users" section in [the redesign
|
|
706
|
+
document](redesign/2_architecture_redesign.md).
|
|
707
|
+
|
|
708
|
+
### 2025-07-07: We Now Use RuboCop
|
|
709
|
+
|
|
710
|
+
To improve code consistency and maintainability, the `ruby-git` project has now
|
|
711
|
+
adopted [RuboCop](https://rubocop.org/) as our static code analyzer and formatter.
|
|
712
|
+
|
|
713
|
+
This integration is a key part of our ongoing commitment to making `ruby-git` a
|
|
714
|
+
high-quality, stable, and easy-to-contribute-to project. All new contributions will
|
|
715
|
+
be expected to adhere to the style guidelines enforced by our RuboCop configuration.
|
|
716
|
+
|
|
717
|
+
RuboCop can be run from the project's Rakefile:
|
|
718
|
+
|
|
719
|
+
```shell
|
|
720
|
+
rake rubocop
|
|
721
|
+
```
|
|
722
|
+
|
|
723
|
+
RuboCop is also run as part of the default rake task (by running `rake`) that is run
|
|
724
|
+
in our Continuous Integration workflow.
|
|
725
|
+
|
|
726
|
+
Going forward, any PRs that have any Robocop offenses will not be merged. In certain
|
|
727
|
+
rare cases, it might be acceptable to disable a RuboCop check for the most limited
|
|
728
|
+
scope possible.
|
|
729
|
+
|
|
730
|
+
If you have a problem fixing a RuboCop offense, don't be afraid to ask a
|
|
731
|
+
contributor.
|
|
732
|
+
|
|
733
|
+
### 2025-06-06: Default Branch Rename
|
|
734
|
+
|
|
735
|
+
On June 6th, 2025, the default branch was renamed from 'master' to 'main'.
|
|
736
|
+
|
|
737
|
+
Instructions for renaming your local or forked branch to match can be found in the
|
|
738
|
+
gist [Default Branch Name
|
|
739
|
+
Change](https://gist.github.com/jcouball/580a10e395f7fdfaaa4297bbe816cc7d).
|
|
740
|
+
|
|
741
|
+
### 2025-05-15: We've Switched to Conventional Commits
|
|
742
|
+
|
|
743
|
+
To enhance our development workflow, enable automated changelog generation, and pave
|
|
744
|
+
the way for Continuous Delivery, the `ruby-git` project has adopted the [Conventional
|
|
745
|
+
Commits standard](https://www.conventionalcommits.org/en/v1.0.0/) for all commit
|
|
746
|
+
messages.
|
|
747
|
+
|
|
748
|
+
Going forward, all commits to this repository **MUST** adhere to the Conventional
|
|
749
|
+
Commits standard. Commits not adhering to this standard will cause the CI build to
|
|
750
|
+
fail. PRs will not be merged if they include non-conventional commits.
|
|
751
|
+
|
|
752
|
+
A git pre-commit hook may be installed to validate your conventional commit messages
|
|
753
|
+
before pushing them to GitHub by running `bin/setup` in the project root.
|
|
426
754
|
|
|
427
|
-
|
|
755
|
+
Read more about this change in the [Commit Message Guidelines section of
|
|
756
|
+
CONTRIBUTING.md](CONTRIBUTING.md#commit-message-guidelines)
|