+172
-172
COMPATIBILITY.md
+172
-172
COMPATIBILITY.md
···
5
5
6
6
## Getting and creating repositories
7
7
8
-
| Feature | Sub-feature | Status | Notes | Examples |
9
-
|---|---|---|---|---|
10
-
| `init` | | ✅ | | |
11
-
| `init` | `--bare` | ✅ | | |
12
-
| `init` | `--template` <br/> `--separate-git-dir` <br/> `--shared` | ❌ | | |
13
-
| `clone` | | ✅ | | - [PlainClone](_examples/clone/main.go) |
14
-
| `clone` | Authentication: <br/> - none <br/> - access token <br/> - username + password <br/> - ssh | ✅ | | - [clone ssh](_examples/clone/auth/ssh/main.go) <br/> - [clone access token](_examples/clone/auth/basic/access_token/main.go) <br/> - [clone user + password](_examples/clone/auth/basic/username_password/main.go) |
15
-
| `clone` | `--progress` <br/> `--single-branch` <br/> `--depth` <br/> `--origin` <br/> `--recurse-submodules` | ✅ | | - [recurse submodules](_examples/clone/main.go) <br/> - [progress](_examples/progress/main.go) |
8
+
| Feature | Sub-feature | Status | Notes | Examples |
9
+
| ------- | ------------------------------------------------------------------------------------------------------------------ | ------ | ----- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
10
+
| `init` | | ✅ | | |
11
+
| `init` | `--bare` | ✅ | | |
12
+
| `init` | `--template` <br/> `--separate-git-dir` <br/> `--shared` | ❌ | | |
13
+
| `clone` | | ✅ | | - [PlainClone](_examples/clone/main.go) |
14
+
| `clone` | Authentication: <br/> - none <br/> - access token <br/> - username + password <br/> - ssh | ✅ | | - [clone ssh](_examples/clone/auth/ssh/main.go) <br/> - [clone access token](_examples/clone/auth/basic/access_token/main.go) <br/> - [clone user + password](_examples/clone/auth/basic/username_password/main.go) |
15
+
| `clone` | `--progress` <br/> `--single-branch` <br/> `--depth` <br/> `--origin` <br/> `--recurse-submodules` <br/>`--shared` | ✅ | | - [recurse submodules](_examples/clone/main.go) <br/> - [progress](_examples/progress/main.go) |
16
16
17
17
## Basic snapshotting
18
18
19
-
| Feature | Sub-feature | Status | Notes | Examples |
20
-
|---|---|---|---|---|
21
-
| `add` | | ✅ | Plain add is supported. Any other flags aren't supported | |
22
-
| `status` | | ✅ | | |
23
-
| `commit` | | ✅ | | - [commit](_examples/commit/main.go) |
24
-
| `reset` | | ✅ | | |
25
-
| `rm` | | ✅ | | |
26
-
| `mv` | | ✅ | | |
19
+
| Feature | Sub-feature | Status | Notes | Examples |
20
+
| -------- | ----------- | ------ | -------------------------------------------------------- | ------------------------------------ |
21
+
| `add` | | ✅ | Plain add is supported. Any other flags aren't supported | |
22
+
| `status` | | ✅ | | |
23
+
| `commit` | | ✅ | | - [commit](_examples/commit/main.go) |
24
+
| `reset` | | ✅ | | |
25
+
| `rm` | | ✅ | | |
26
+
| `mv` | | ✅ | | |
27
27
28
28
## Branching and merging
29
29
30
-
| Feature | Sub-feature | Status | Notes | Examples |
31
-
|---|---|---|---|---|
32
-
| `branch` | | ✅ | | - [branch](_examples/branch/main.go) |
33
-
| `checkout` | | ✅ | Basic usages of checkout are supported. | - [checkout](_examples/checkout/main.go) |
34
-
| `merge` | | ❌ | | |
35
-
| `mergetool` | | ❌ | | |
36
-
| `stash` | | ❌ | | |
37
-
| `tag` | | ✅ | | - [tag](_examples/tag/main.go) <br/> - [tag create and push](_examples/tag-create-push/main.go) |
30
+
| Feature | Sub-feature | Status | Notes | Examples |
31
+
| ----------- | ----------- | ------ | --------------------------------------- | ----------------------------------------------------------------------------------------------- |
32
+
| `branch` | | ✅ | | - [branch](_examples/branch/main.go) |
33
+
| `checkout` | | ✅ | Basic usages of checkout are supported. | - [checkout](_examples/checkout/main.go) |
34
+
| `merge` | | ❌ | | |
35
+
| `mergetool` | | ❌ | | |
36
+
| `stash` | | ❌ | | |
37
+
| `tag` | | ✅ | | - [tag](_examples/tag/main.go) <br/> - [tag create and push](_examples/tag-create-push/main.go) |
38
38
39
39
## Sharing and updating projects
40
40
41
-
| Feature | Sub-feature | Status | Notes | Examples |
42
-
|---|---|---|---|---|
43
-
| `fetch` | | ✅ | | |
44
-
| `pull` | | ✅ | Only supports merges where the merge can be resolved as a fast-forward. | - [pull](_examples/pull/main.go) |
45
-
| `push` | | ✅ | | - [push](_examples/push/main.go) |
46
-
| `remote` | | ✅ | | - [remotes](_examples/remotes/main.go) |
47
-
| `submodule` | | ✅ | | - [submodule](_examples/submodule/main.go) |
48
-
| `submodule` | deinit | ❌ | | |
41
+
| Feature | Sub-feature | Status | Notes | Examples |
42
+
| ----------- | ----------- | ------ | ----------------------------------------------------------------------- | ------------------------------------------ |
43
+
| `fetch` | | ✅ | | |
44
+
| `pull` | | ✅ | Only supports merges where the merge can be resolved as a fast-forward. | - [pull](_examples/pull/main.go) |
45
+
| `push` | | ✅ | | - [push](_examples/push/main.go) |
46
+
| `remote` | | ✅ | | - [remotes](_examples/remotes/main.go) |
47
+
| `submodule` | | ✅ | | - [submodule](_examples/submodule/main.go) |
48
+
| `submodule` | deinit | ❌ | | |
49
49
50
50
## Inspection and comparison
51
51
52
-
| Feature | Sub-feature | Status | Notes | Examples |
53
-
|---|---|---|---|---|
54
-
| `show` | | ✅ | | |
55
-
| `log` | | ✅ | | - [log](_examples/log/main.go) |
56
-
| `shortlog` | | (see log) | | |
57
-
| `describe` | | ❌ | | |
52
+
| Feature | Sub-feature | Status | Notes | Examples |
53
+
| ---------- | ----------- | --------- | ----- | ------------------------------ |
54
+
| `show` | | ✅ | | |
55
+
| `log` | | ✅ | | - [log](_examples/log/main.go) |
56
+
| `shortlog` | | (see log) | | |
57
+
| `describe` | | ❌ | | |
58
58
59
59
## Patching
60
60
61
-
| Feature | Sub-feature | Status | Notes | Examples |
62
-
|---|---|---|---|---|
63
-
| `apply` | | ❌ | | |
64
-
| `cherry-pick` | | ❌ | | |
65
-
| `diff` | | ✅ | Patch object with UnifiedDiff output representation. | |
66
-
| `rebase` | | ❌ | | |
67
-
| `revert` | | ❌ | | |
61
+
| Feature | Sub-feature | Status | Notes | Examples |
62
+
| ------------- | ----------- | ------ | ---------------------------------------------------- | -------- |
63
+
| `apply` | | ❌ | | |
64
+
| `cherry-pick` | | ❌ | | |
65
+
| `diff` | | ✅ | Patch object with UnifiedDiff output representation. | |
66
+
| `rebase` | | ❌ | | |
67
+
| `revert` | | ❌ | | |
68
68
69
69
## Debugging
70
70
71
-
| Feature | Sub-feature | Status | Notes | Examples |
72
-
|---|---|---|---|---|
73
-
| `bisect` | | ❌ | | |
74
-
| `blame` | | ✅ | | - [blame](_examples/blame/main.go) |
75
-
| `grep` | | ✅ | | |
71
+
| Feature | Sub-feature | Status | Notes | Examples |
72
+
| -------- | ----------- | ------ | ----- | ---------------------------------- |
73
+
| `bisect` | | ❌ | | |
74
+
| `blame` | | ✅ | | - [blame](_examples/blame/main.go) |
75
+
| `grep` | | ✅ | | |
76
76
77
77
## Email
78
78
79
-
| Feature | Sub-feature | Status | Notes | Examples |
80
-
|---|---|---|---|---|
81
-
| `am` | | ❌ | | |
82
-
| `apply` | | ❌ | | |
83
-
| `format-patch` | | ❌ | | |
84
-
| `send-email` | | ❌ | | |
85
-
| `request-pull` | | ❌ | | |
79
+
| Feature | Sub-feature | Status | Notes | Examples |
80
+
| -------------- | ----------- | ------ | ----- | -------- |
81
+
| `am` | | ❌ | | |
82
+
| `apply` | | ❌ | | |
83
+
| `format-patch` | | ❌ | | |
84
+
| `send-email` | | ❌ | | |
85
+
| `request-pull` | | ❌ | | |
86
86
87
87
## External systems
88
88
89
-
| Feature | Sub-feature | Status | Notes | Examples |
90
-
|---|---|---|---|---|
91
-
| `svn` | | ❌ | | |
92
-
| `fast-import` | | ❌ | | |
93
-
| `lfs` | | ❌ | | |
89
+
| Feature | Sub-feature | Status | Notes | Examples |
90
+
| ------------- | ----------- | ------ | ----- | -------- |
91
+
| `svn` | | ❌ | | |
92
+
| `fast-import` | | ❌ | | |
93
+
| `lfs` | | ❌ | | |
94
94
95
95
## Administration
96
96
97
-
| Feature | Sub-feature | Status | Notes | Examples |
98
-
|---|---|---|---|---|
99
-
| `clean` | | ✅ | | |
100
-
| `gc` | | ❌ | | |
101
-
| `fsck` | | ❌ | | |
102
-
| `reflog` | | ❌ | | |
103
-
| `filter-branch` | | ❌ | | |
104
-
| `instaweb` | | ❌ | | |
105
-
| `archive` | | ❌ | | |
106
-
| `bundle` | | ❌ | | |
107
-
| `prune` | | ❌ | | |
108
-
| `repack` | | ❌ | | |
97
+
| Feature | Sub-feature | Status | Notes | Examples |
98
+
| --------------- | ----------- | ------ | ----- | -------- |
99
+
| `clean` | | ✅ | | |
100
+
| `gc` | | ❌ | | |
101
+
| `fsck` | | ❌ | | |
102
+
| `reflog` | | ❌ | | |
103
+
| `filter-branch` | | ❌ | | |
104
+
| `instaweb` | | ❌ | | |
105
+
| `archive` | | ❌ | | |
106
+
| `bundle` | | ❌ | | |
107
+
| `prune` | | ❌ | | |
108
+
| `repack` | | ❌ | | |
109
109
110
110
## Server admin
111
111
112
-
| Feature | Sub-feature | Status | Notes | Examples |
113
-
|---|---|---|---|---|
114
-
| `daemon` | | ❌ | | |
115
-
| `update-server-info` | | ❌ | | |
112
+
| Feature | Sub-feature | Status | Notes | Examples |
113
+
| -------------------- | ----------- | ------ | ----- | -------- |
114
+
| `daemon` | | ❌ | | |
115
+
| `update-server-info` | | ❌ | | |
116
116
117
117
## Advanced
118
118
119
-
| Feature | Sub-feature | Status | Notes | Examples |
120
-
|---|---|---|---|---|
121
-
| `notes` | | ❌ | | |
122
-
| `replace` | | ❌ | | |
123
-
| `worktree` | | ❌ | | |
124
-
| `annotate` | | (see blame) | | |
119
+
| Feature | Sub-feature | Status | Notes | Examples |
120
+
| ---------- | ----------- | ----------- | ----- | -------- |
121
+
| `notes` | | ❌ | | |
122
+
| `replace` | | ❌ | | |
123
+
| `worktree` | | ❌ | | |
124
+
| `annotate` | | (see blame) | | |
125
125
126
126
## GPG
127
127
128
-
| Feature | Sub-feature | Status | Notes | Examples |
129
-
|---|---|---|---|---|
130
-
| `git-verify-commit` | | ✅ | | |
131
-
| `git-verify-tag` | | ✅ | | |
128
+
| Feature | Sub-feature | Status | Notes | Examples |
129
+
| ------------------- | ----------- | ------ | ----- | -------- |
130
+
| `git-verify-commit` | | ✅ | | |
131
+
| `git-verify-tag` | | ✅ | | |
132
132
133
133
## Plumbing commands
134
134
135
-
| Feature | Sub-feature | Status | Notes | Examples |
136
-
|---|---|---|---|---|
137
-
| `cat-file` | | ✅ | | |
138
-
| `check-ignore` | | ❌ | | |
139
-
| `commit-tree` | | ❌ | | |
140
-
| `count-objects` | | ❌ | | |
141
-
| `diff-index` | | ❌ | | |
142
-
| `for-each-ref` | | ✅ | | |
143
-
| `hash-object` | | ✅ | | |
144
-
| `ls-files` | | ✅ | | |
145
-
| `ls-remote` | | ✅ | | - [ls-remote](_examples/ls-remote/main.go) |
146
-
| `merge-base` | `--independent` <br/> `--is-ancestor` | ⚠️ (partial) | Calculates the merge-base only between two commits. | - [merge-base](_examples/merge_base/main.go) |
147
-
| `merge-base` | `--fork-point` <br/> `--octopus` | ❌ | | |
148
-
| `read-tree` | | ❌ | | |
149
-
| `rev-list` | | ✅ | | |
150
-
| `rev-parse` | | ❌ | | |
151
-
| `show-ref` | | ✅ | | |
152
-
| `symbolic-ref` | | ✅ | | |
153
-
| `update-index` | | ❌ | | |
154
-
| `update-ref` | | ❌ | | |
155
-
| `verify-pack` | | ❌ | | |
156
-
| `write-tree` | | ❌ | | |
135
+
| Feature | Sub-feature | Status | Notes | Examples |
136
+
| --------------- | ------------------------------------- | ------------ | --------------------------------------------------- | -------------------------------------------- |
137
+
| `cat-file` | | ✅ | | |
138
+
| `check-ignore` | | ❌ | | |
139
+
| `commit-tree` | | ❌ | | |
140
+
| `count-objects` | | ❌ | | |
141
+
| `diff-index` | | ❌ | | |
142
+
| `for-each-ref` | | ✅ | | |
143
+
| `hash-object` | | ✅ | | |
144
+
| `ls-files` | | ✅ | | |
145
+
| `ls-remote` | | ✅ | | - [ls-remote](_examples/ls-remote/main.go) |
146
+
| `merge-base` | `--independent` <br/> `--is-ancestor` | ⚠️ (partial) | Calculates the merge-base only between two commits. | - [merge-base](_examples/merge_base/main.go) |
147
+
| `merge-base` | `--fork-point` <br/> `--octopus` | ❌ | | |
148
+
| `read-tree` | | ❌ | | |
149
+
| `rev-list` | | ✅ | | |
150
+
| `rev-parse` | | ❌ | | |
151
+
| `show-ref` | | ✅ | | |
152
+
| `symbolic-ref` | | ✅ | | |
153
+
| `update-index` | | ❌ | | |
154
+
| `update-ref` | | ❌ | | |
155
+
| `verify-pack` | | ❌ | | |
156
+
| `write-tree` | | ❌ | | |
157
157
158
158
## Indexes and Git Protocols
159
159
160
-
| Feature | Version | Status | Notes |
161
-
|---|---|---|---|
162
-
| index | [v1](https://github.com/git/git/blob/master/Documentation/gitformat-index.txt) | ❌ | |
163
-
| index | [v2](https://github.com/git/git/blob/master/Documentation/gitformat-index.txt) | ✅ | |
164
-
| index | [v3](https://github.com/git/git/blob/master/Documentation/gitformat-index.txt) | ❌ | |
165
-
| pack-protocol | [v1](https://github.com/git/git/blob/master/Documentation/gitprotocol-pack.txt) | ✅ | |
166
-
| pack-protocol | [v2](https://github.com/git/git/blob/master/Documentation/gitprotocol-v2.txt) | ❌ | |
167
-
| multi-pack-index | [v1](https://github.com/git/git/blob/master/Documentation/gitformat-pack.txt) | ❌ | |
168
-
| pack-*.rev files | [v1](https://github.com/git/git/blob/master/Documentation/gitformat-pack.txt) | ❌ | |
169
-
| pack-*.mtimes files | [v1](https://github.com/git/git/blob/master/Documentation/gitformat-pack.txt) | ❌ | |
170
-
| cruft packs | | ❌ | |
160
+
| Feature | Version | Status | Notes |
161
+
| -------------------- | ------------------------------------------------------------------------------- | ------ | ----- |
162
+
| index | [v1](https://github.com/git/git/blob/master/Documentation/gitformat-index.txt) | ❌ | |
163
+
| index | [v2](https://github.com/git/git/blob/master/Documentation/gitformat-index.txt) | ✅ | |
164
+
| index | [v3](https://github.com/git/git/blob/master/Documentation/gitformat-index.txt) | ❌ | |
165
+
| pack-protocol | [v1](https://github.com/git/git/blob/master/Documentation/gitprotocol-pack.txt) | ✅ | |
166
+
| pack-protocol | [v2](https://github.com/git/git/blob/master/Documentation/gitprotocol-v2.txt) | ❌ | |
167
+
| multi-pack-index | [v1](https://github.com/git/git/blob/master/Documentation/gitformat-pack.txt) | ❌ | |
168
+
| pack-\*.rev files | [v1](https://github.com/git/git/blob/master/Documentation/gitformat-pack.txt) | ❌ | |
169
+
| pack-\*.mtimes files | [v1](https://github.com/git/git/blob/master/Documentation/gitformat-pack.txt) | ❌ | |
170
+
| cruft packs | | ❌ | |
171
171
172
172
## Capabilities
173
173
174
-
| Feature | Status | Notes |
175
-
|---|---|---|
176
-
| `multi_ack` | ❌ | |
177
-
| `multi_ack_detailed` | ❌ | |
178
-
| `no-done` | ❌ | |
179
-
| `thin-pack` | ❌ | |
180
-
| `side-band` | ⚠️ (partial) | |
181
-
| `side-band-64k` | ⚠️ (partial) | |
182
-
| `ofs-delta` | ✅ | |
183
-
| `agent` | ✅ | |
184
-
| `object-format` | ❌ | |
185
-
| `symref` | ✅ | |
186
-
| `shallow` | ✅ | |
187
-
| `deepen-since` | ✅ | |
188
-
| `deepen-not` | ❌ | |
189
-
| `deepen-relative` | ❌ | |
190
-
| `no-progress` | ✅ | |
191
-
| `include-tag` | ✅ | |
192
-
| `report-status` | ✅ | |
193
-
| `report-status-v2` | ❌ | |
194
-
| `delete-refs` | ✅ | |
195
-
| `quiet` | ❌ | |
196
-
| `atomic` | ✅ | |
197
-
| `push-options` | ✅ | |
198
-
| `allow-tip-sha1-in-want` | ✅ | |
199
-
| `allow-reachable-sha1-in-want` | ❌ | |
200
-
| `push-cert=<nonce>` | ❌ | |
201
-
| `filter` | ❌ | |
202
-
| `session-id=<session id>` | ❌ | |
174
+
| Feature | Status | Notes |
175
+
| ------------------------------ | ------------ | ----- |
176
+
| `multi_ack` | ❌ | |
177
+
| `multi_ack_detailed` | ❌ | |
178
+
| `no-done` | ❌ | |
179
+
| `thin-pack` | ❌ | |
180
+
| `side-band` | ⚠️ (partial) | |
181
+
| `side-band-64k` | ⚠️ (partial) | |
182
+
| `ofs-delta` | ✅ | |
183
+
| `agent` | ✅ | |
184
+
| `object-format` | ❌ | |
185
+
| `symref` | ✅ | |
186
+
| `shallow` | ✅ | |
187
+
| `deepen-since` | ✅ | |
188
+
| `deepen-not` | ❌ | |
189
+
| `deepen-relative` | ❌ | |
190
+
| `no-progress` | ✅ | |
191
+
| `include-tag` | ✅ | |
192
+
| `report-status` | ✅ | |
193
+
| `report-status-v2` | ❌ | |
194
+
| `delete-refs` | ✅ | |
195
+
| `quiet` | ❌ | |
196
+
| `atomic` | ✅ | |
197
+
| `push-options` | ✅ | |
198
+
| `allow-tip-sha1-in-want` | ✅ | |
199
+
| `allow-reachable-sha1-in-want` | ❌ | |
200
+
| `push-cert=<nonce>` | ❌ | |
201
+
| `filter` | ❌ | |
202
+
| `session-id=<session id>` | ❌ | |
203
203
204
204
## Transport Schemes
205
205
206
-
| Scheme | Status | Notes | Examples |
207
-
|---|---|---|---|
208
-
| `http(s)://` (dumb) | ❌ | | |
209
-
| `http(s)://` (smart) | ✅ | | |
210
-
| `git://` | ✅ | | |
211
-
| `ssh://` | ✅ | | |
212
-
| `file://` | ⚠️ (partial) | Warning: this is not pure Golang. This shells out to the `git` binary. | |
213
-
| Custom | ✅ | All existing schemes can be replaced by custom implementations. | - [custom_http](_examples/custom_http/main.go) |
206
+
| Scheme | Status | Notes | Examples |
207
+
| -------------------- | ------------ | ---------------------------------------------------------------------- | ---------------------------------------------- |
208
+
| `http(s)://` (dumb) | ❌ | | |
209
+
| `http(s)://` (smart) | ✅ | | |
210
+
| `git://` | ✅ | | |
211
+
| `ssh://` | ✅ | | |
212
+
| `file://` | ⚠️ (partial) | Warning: this is not pure Golang. This shells out to the `git` binary. | |
213
+
| Custom | ✅ | All existing schemes can be replaced by custom implementations. | - [custom_http](_examples/custom_http/main.go) |
214
214
215
215
## SHA256
216
216
217
-
| Feature | Sub-feature | Status | Notes | Examples |
218
-
|---|---|---|---|---|
219
-
| `init` | | ✅ | Requires building with tag sha256. | - [init](_examples/sha256/main.go) |
220
-
| `commit` | | ✅ | Requires building with tag sha256. | - [commit](_examples/sha256/main.go) |
221
-
| `pull` | | ❌ | | |
222
-
| `fetch` | | ❌ | | |
223
-
| `push` | | ❌ | | |
217
+
| Feature | Sub-feature | Status | Notes | Examples |
218
+
| -------- | ----------- | ------ | ---------------------------------- | ------------------------------------ |
219
+
| `init` | | ✅ | Requires building with tag sha256. | - [init](_examples/sha256/main.go) |
220
+
| `commit` | | ✅ | Requires building with tag sha256. | - [commit](_examples/sha256/main.go) |
221
+
| `pull` | | ❌ | | |
222
+
| `fetch` | | ❌ | | |
223
+
| `push` | | ❌ | | |
224
224
225
225
## Other features
226
226
227
-
| Feature | Sub-feature | Status | Notes | Examples |
228
-
|---|---|---|---|---|
229
-
| `config` | `--local` | ✅ | Read and write per-repository (`.git/config`). | |
230
-
| `config` | `--global` <br/> `--system` | ✅ | Read-only. | |
231
-
| `gitignore` | | ✅ | | |
232
-
| `gitattributes` | | ✅ | | |
233
-
| `git-worktree` | | ❌ | Multiple worktrees are not supported. | |
227
+
| Feature | Sub-feature | Status | Notes | Examples |
228
+
| --------------- | --------------------------- | ------ | ---------------------------------------------- | -------- |
229
+
| `config` | `--local` | ✅ | Read and write per-repository (`.git/config`). | |
230
+
| `config` | `--global` <br/> `--system` | ✅ | Read-only. | |
231
+
| `gitignore` | | ✅ | | |
232
+
| `gitattributes` | | ✅ | | |
233
+
| `git-worktree` | | ❌ | Multiple worktrees are not supported. | |
+9
options.go
+9
options.go
···
78
78
CABundle []byte
79
79
// ProxyOptions provides info required for connecting to a proxy.
80
80
ProxyOptions transport.ProxyOptions
81
+
// When the repository to clone is on the local machine, instead of
82
+
// using hard links, automatically setup .git/objects/info/alternates
83
+
// to share the objects with the source repository.
84
+
// The resulting repository starts out without any object of its own.
85
+
// NOTE: this is a possibly dangerous operation; do not use it unless
86
+
// you understand what it does.
87
+
//
88
+
// [Reference]: https://git-scm.com/docs/git-clone#Documentation/git-clone.txt---shared
89
+
Shared bool
81
90
}
82
91
83
92
// Validate validates the fields and sets the default values.
+1
plumbing/storer/object.go
+1
plumbing/storer/object.go
···
42
42
HasEncodedObject(plumbing.Hash) error
43
43
// EncodedObjectSize returns the plaintext size of the encoded object.
44
44
EncodedObjectSize(plumbing.Hash) (int64, error)
45
+
AddAlternate(remote string) error
45
46
}
46
47
47
48
// DeltaObjectStorer is an EncodedObjectStorer that can return delta
+4
plumbing/storer/object_test.go
+4
plumbing/storer/object_test.go
+26
repository.go
+26
repository.go
···
22
22
"github.com/go-git/go-git/v5/config"
23
23
"github.com/go-git/go-git/v5/internal/path_util"
24
24
"github.com/go-git/go-git/v5/internal/revision"
25
+
"github.com/go-git/go-git/v5/internal/url"
25
26
"github.com/go-git/go-git/v5/plumbing"
26
27
"github.com/go-git/go-git/v5/plumbing/cache"
27
28
formatcfg "github.com/go-git/go-git/v5/plumbing/format/config"
···
62
63
ErrUnableToResolveCommit = errors.New("unable to resolve commit")
63
64
ErrPackedObjectsNotSupported = errors.New("packed objects not supported")
64
65
ErrSHA256NotSupported = errors.New("go-git was not compiled with SHA256 support")
66
+
ErrAlternatePathNotSupported = errors.New("alternate path must use the file scheme")
65
67
)
66
68
67
69
// Repository represents a git repository
···
885
887
886
888
if _, err := r.CreateRemote(c); err != nil {
887
889
return err
890
+
}
891
+
892
+
// When the repository to clone is on the local machine,
893
+
// instead of using hard links, automatically setup .git/objects/info/alternates
894
+
// to share the objects with the source repository
895
+
if o.Shared {
896
+
if !url.IsLocalEndpoint(o.URL) {
897
+
return ErrAlternatePathNotSupported
898
+
}
899
+
altpath := o.URL
900
+
remoteRepo, err := PlainOpen(o.URL)
901
+
if err != nil {
902
+
return fmt.Errorf("failed to open remote repository: %w", err)
903
+
}
904
+
conf, err := remoteRepo.Config()
905
+
if err != nil {
906
+
return fmt.Errorf("failed to read remote repository configuration: %w", err)
907
+
}
908
+
if !conf.Core.IsBare {
909
+
altpath = path.Join(altpath, GitDirName)
910
+
}
911
+
if err := r.Storer.AddAlternate(altpath); err != nil {
912
+
return fmt.Errorf("failed to add alternate file to git objects dir: %w", err)
913
+
}
888
914
}
889
915
890
916
ref, err := r.fetchAndUpdateReferences(ctx, &FetchOptions{
+96
repository_test.go
+96
repository_test.go
···
9
9
"os"
10
10
"os/exec"
11
11
"os/user"
12
+
"path"
12
13
"path/filepath"
13
14
"regexp"
14
15
"strings"
···
789
790
c.Assert(err, IsNil)
790
791
c.Assert(cfg.Branches, HasLen, 1)
791
792
c.Assert(cfg.Branches["master"].Name, Equals, "master")
793
+
}
794
+
795
+
func (s *RepositorySuite) TestPlainCloneBareAndShared(c *C) {
796
+
dir, clean := s.TemporalDir()
797
+
defer clean()
798
+
799
+
remote := s.GetBasicLocalRepositoryURL()
800
+
801
+
r, err := PlainClone(dir, true, &CloneOptions{
802
+
URL: remote,
803
+
Shared: true,
804
+
})
805
+
c.Assert(err, IsNil)
806
+
807
+
altpath := path.Join(dir, "objects", "info", "alternates")
808
+
_, err = os.Stat(altpath)
809
+
c.Assert(err, IsNil)
810
+
811
+
data, err := os.ReadFile(altpath)
812
+
c.Assert(err, IsNil)
813
+
814
+
line := path.Join(remote, GitDirName, "objects") + "\n"
815
+
c.Assert(string(data), Equals, line)
816
+
817
+
cfg, err := r.Config()
818
+
c.Assert(err, IsNil)
819
+
c.Assert(cfg.Branches, HasLen, 1)
820
+
c.Assert(cfg.Branches["master"].Name, Equals, "master")
821
+
}
822
+
823
+
func (s *RepositorySuite) TestPlainCloneShared(c *C) {
824
+
dir, clean := s.TemporalDir()
825
+
defer clean()
826
+
827
+
remote := s.GetBasicLocalRepositoryURL()
828
+
829
+
r, err := PlainClone(dir, false, &CloneOptions{
830
+
URL: remote,
831
+
Shared: true,
832
+
})
833
+
c.Assert(err, IsNil)
834
+
835
+
altpath := path.Join(dir, GitDirName, "objects", "info", "alternates")
836
+
_, err = os.Stat(altpath)
837
+
c.Assert(err, IsNil)
838
+
839
+
data, err := os.ReadFile(altpath)
840
+
c.Assert(err, IsNil)
841
+
842
+
line := path.Join(remote, GitDirName, "objects") + "\n"
843
+
c.Assert(string(data), Equals, line)
844
+
845
+
cfg, err := r.Config()
846
+
c.Assert(err, IsNil)
847
+
c.Assert(cfg.Branches, HasLen, 1)
848
+
c.Assert(cfg.Branches["master"].Name, Equals, "master")
849
+
}
850
+
851
+
func (s *RepositorySuite) TestPlainCloneSharedHttpShouldReturnError(c *C) {
852
+
dir, clean := s.TemporalDir()
853
+
defer clean()
854
+
855
+
remote := "http://somerepo"
856
+
857
+
_, err := PlainClone(dir, false, &CloneOptions{
858
+
URL: remote,
859
+
Shared: true,
860
+
})
861
+
c.Assert(err, Equals, ErrAlternatePathNotSupported)
862
+
}
863
+
864
+
func (s *RepositorySuite) TestPlainCloneSharedHttpsShouldReturnError(c *C) {
865
+
dir, clean := s.TemporalDir()
866
+
defer clean()
867
+
868
+
remote := "https://somerepo"
869
+
870
+
_, err := PlainClone(dir, false, &CloneOptions{
871
+
URL: remote,
872
+
Shared: true,
873
+
})
874
+
c.Assert(err, Equals, ErrAlternatePathNotSupported)
875
+
}
876
+
877
+
func (s *RepositorySuite) TestPlainCloneSharedSSHShouldReturnError(c *C) {
878
+
dir, clean := s.TemporalDir()
879
+
defer clean()
880
+
881
+
remote := "ssh://somerepo"
882
+
883
+
_, err := PlainClone(dir, false, &CloneOptions{
884
+
URL: remote,
885
+
Shared: true,
886
+
})
887
+
c.Assert(err, Equals, ErrAlternatePathNotSupported)
792
888
}
793
889
794
890
func (s *RepositorySuite) TestPlainCloneWithRemoteName(c *C) {
+32
-1
storage/filesystem/dotgit/dotgit.go
+32
-1
storage/filesystem/dotgit/dotgit.go
···
8
8
"fmt"
9
9
"io"
10
10
"os"
11
+
"path"
11
12
"path/filepath"
13
+
"runtime"
12
14
"sort"
13
15
"strings"
14
16
"time"
···
38
40
remotesPath = "remotes"
39
41
logsPath = "logs"
40
42
worktreesPath = "worktrees"
43
+
alternatesPath = "alternates"
41
44
42
45
tmpPackedRefsPrefix = "._packed-refs"
43
46
···
1105
1108
return d.fs.Chroot(d.fs.Join(modulePath, name))
1106
1109
}
1107
1110
1111
+
func (d *DotGit) AddAlternate(remote string) error {
1112
+
altpath := d.fs.Join(objectsPath, infoPath, alternatesPath)
1113
+
1114
+
f, err := d.fs.OpenFile(altpath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0640)
1115
+
if err != nil {
1116
+
return fmt.Errorf("cannot open file: %w", err)
1117
+
}
1118
+
defer f.Close()
1119
+
1120
+
// locking in windows throws an error, based on comments
1121
+
// https://github.com/go-git/go-git/pull/860#issuecomment-1751823044
1122
+
// do not lock on windows platform.
1123
+
if runtime.GOOS != "windows" {
1124
+
if err = f.Lock(); err != nil {
1125
+
return fmt.Errorf("cannot lock file: %w", err)
1126
+
}
1127
+
defer f.Unlock()
1128
+
}
1129
+
1130
+
line := path.Join(remote, objectsPath) + "\n"
1131
+
_, err = io.WriteString(f, line)
1132
+
if err != nil {
1133
+
return fmt.Errorf("error writing 'alternates' file: %w", err)
1134
+
}
1135
+
1136
+
return nil
1137
+
}
1138
+
1108
1139
// Alternates returns DotGit(s) based off paths in objects/info/alternates if
1109
1140
// available. This can be used to checks if it's a shared repository.
1110
1141
func (d *DotGit) Alternates() ([]*DotGit, error) {
1111
-
altpath := d.fs.Join("objects", "info", "alternates")
1142
+
altpath := d.fs.Join(objectsPath, infoPath, alternatesPath)
1112
1143
f, err := d.fs.Open(altpath)
1113
1144
if err != nil {
1114
1145
return nil, err
+4
storage/filesystem/storage.go
+4
storage/filesystem/storage.go
+4
storage/memory/storage.go
+4
storage/memory/storage.go