aboutsummaryrefslogtreecommitdiffstats
path: root/options.go
blob: 84744c745ad3ce817d791737860d5bd966abce3d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
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
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
package git

import (
	"errors"
	"fmt"
	"regexp"
	"strings"
	"time"

	"github.com/ProtonMail/go-crypto/openpgp"
	"github.com/go-git/go-git/v5/config"
	"github.com/go-git/go-git/v5/plumbing"
	formatcfg "github.com/go-git/go-git/v5/plumbing/format/config"
	"github.com/go-git/go-git/v5/plumbing/object"
	"github.com/go-git/go-git/v5/plumbing/protocol/packp/sideband"
	"github.com/go-git/go-git/v5/plumbing/transport"
)

// SubmoduleRescursivity defines how depth will affect any submodule recursive
// operation.
type SubmoduleRescursivity uint

const (
	// DefaultRemoteName name of the default Remote, just like git command.
	DefaultRemoteName = "origin"

	// NoRecurseSubmodules disables the recursion for a submodule operation.
	NoRecurseSubmodules SubmoduleRescursivity = 0
	// DefaultSubmoduleRecursionDepth allow recursion in a submodule operation.
	DefaultSubmoduleRecursionDepth SubmoduleRescursivity = 10
)

var (
	ErrMissingURL = errors.New("URL field is required")
)

// CloneOptions describes how a clone should be performed.
type CloneOptions struct {
	// The (possibly remote) repository URL to clone from.
	URL string
	// Auth credentials, if required, to use with the remote repository.
	Auth transport.AuthMethod
	// Name of the remote to be added, by default `origin`.
	RemoteName string
	// Remote branch to clone.
	ReferenceName plumbing.ReferenceName
	// Fetch only ReferenceName if true.
	SingleBranch bool
	// Mirror clones the repository as a mirror.
	//
	// Compared to a bare clone, mirror not only maps local branches of the
	// source to local branches of the target, it maps all refs (including
	// remote-tracking branches, notes etc.) and sets up a refspec configuration
	// such that all these refs are overwritten by a git remote update in the
	// target repository.
	Mirror bool
	// No checkout of HEAD after clone if true.
	NoCheckout bool
	// Limit fetching to the specified number of commits.
	Depth int
	// RecurseSubmodules after the clone is created, initialize all submodules
	// within, using their default settings. This option is ignored if the
	// cloned repository does not have a worktree.
	RecurseSubmodules SubmoduleRescursivity
	// Progress is where the human readable information sent by the server is
	// stored, if nil nothing is stored and the capability (if supported)
	// no-progress, is sent to the server to avoid send this information.
	Progress sideband.Progress
	// Tags describe how the tags will be fetched from the remote repository,
	// by default is AllTags.
	Tags TagMode
	// InsecureSkipTLS skips ssl verify if protocol is https
	InsecureSkipTLS bool
	// CABundle specify additional ca bundle with system cert pool
	CABundle []byte
	// ProxyOptions provides info required for connecting to a proxy.
	ProxyOptions transport.ProxyOptions
}

// Validate validates the fields and sets the default values.
func (o *CloneOptions) Validate() error {
	if o.URL == "" {
		return ErrMissingURL
	}

	if o.RemoteName == "" {
		o.RemoteName = DefaultRemoteName
	}

	if o.ReferenceName == "" {
		o.ReferenceName = plumbing.HEAD
	}

	if o.Tags == InvalidTagMode {
		o.Tags = AllTags
	}

	return nil
}

// PullOptions describes how a pull should be performed.
type PullOptions struct {
	// Name of the remote to be pulled. If empty, uses the default.
	RemoteName string
	// RemoteURL overrides the remote repo address with a custom URL
	RemoteURL string
	// Remote branch to clone. If empty, uses HEAD.
	ReferenceName plumbing.ReferenceName
	// Fetch only ReferenceName if true.
	SingleBranch bool
	// Limit fetching to the specified number of commits.
	Depth int
	// Auth credentials, if required, to use with the remote repository.
	Auth transport.AuthMethod
	// RecurseSubmodules controls if new commits of all populated submodules
	// should be fetched too.
	RecurseSubmodules SubmoduleRescursivity
	// Progress is where the human readable information sent by the server is
	// stored, if nil nothing is stored and the capability (if supported)
	// no-progress, is sent to the server to avoid send this information.
	Progress sideband.Progress
	// Force allows the pull to update a local branch even when the remote
	// branch does not descend from it.
	Force bool
	// InsecureSkipTLS skips ssl verify if protocol is https
	InsecureSkipTLS bool
	// CABundle specify additional ca bundle with system cert pool
	CABundle []byte
	// ProxyOptions provides info required for connecting to a proxy.
	ProxyOptions transport.ProxyOptions
}

// Validate validates the fields and sets the default values.
func (o *PullOptions) Validate() error {
	if o.RemoteName == "" {
		o.RemoteName = DefaultRemoteName
	}

	if o.ReferenceName == "" {
		o.ReferenceName = plumbing.HEAD
	}

	return nil
}

type TagMode int

const (
	InvalidTagMode TagMode = iota
	// TagFollowing any tag that points into the histories being fetched is also
	// fetched. TagFollowing requires a server with `include-tag` capability
	// in order to fetch the annotated tags objects.
	TagFollowing
	// AllTags fetch all tags from the remote (i.e., fetch remote tags
	// refs/tags/* into local tags with the same name)
	AllTags
	//NoTags fetch no tags from the remote at all
	NoTags
)

// FetchOptions describes how a fetch should be performed
type FetchOptions struct {
	// Name of the remote to fetch from. Defaults to origin.
	RemoteName string
	// RemoteURL overrides the remote repo address with a custom URL
	RemoteURL string
	RefSpecs  []config.RefSpec
	// Depth limit fetching to the specified number of commits from the tip of
	// each remote branch history.
	Depth int
	// Auth credentials, if required, to use with the remote repository.
	Auth transport.AuthMethod
	// Progress is where the human readable information sent by the server is
	// stored, if nil nothing is stored and the capability (if supported)
	// no-progress, is sent to the server to avoid send this information.
	Progress sideband.Progress
	// Tags describe how the tags will be fetched from the remote repository,
	// by default is TagFollowing.
	Tags TagMode
	// Force allows the fetch to update a local branch even when the remote
	// branch does not descend from it.
	Force bool
	// InsecureSkipTLS skips ssl verify if protocol is https
	InsecureSkipTLS bool
	// CABundle specify additional ca bundle with system cert pool
	CABundle []byte
	// ProxyOptions provides info required for connecting to a proxy.
	ProxyOptions transport.ProxyOptions
}

// Validate validates the fields and sets the default values.
func (o *FetchOptions) Validate() error {
	if o.RemoteName == "" {
		o.RemoteName = DefaultRemoteName
	}

	if o.Tags == InvalidTagMode {
		o.Tags = TagFollowing
	}

	for _, r := range o.RefSpecs {
		if err := r.Validate(); err != nil {
			return err
		}
	}

	return nil
}

// PushOptions describes how a push should be performed.
type PushOptions struct {
	// RemoteName is the name of the remote to be pushed to.
	RemoteName string
	// RemoteURL overrides the remote repo address with a custom URL
	RemoteURL string
	// RefSpecs specify what destination ref to update with what source object.
	//
	// The format of a <refspec> parameter is an optional plus +, followed by
	//  the source object <src>, followed by a colon :, followed by the destination ref <dst>.
	// The <src> is often the name of the branch you would want to push, but it can be a SHA-1.
	// The <dst> tells which ref on the remote side is updated with this push.
	//
	// A refspec with empty src can be used to delete a reference.
	RefSpecs []config.RefSpec
	// Auth credentials, if required, to use with the remote repository.
	Auth transport.AuthMethod
	// Progress is where the human readable information sent by the server is
	// stored, if nil nothing is stored.
	Progress sideband.Progress
	// Prune specify that remote refs that match given RefSpecs and that do
	// not exist locally will be removed.
	Prune bool
	// Force allows the push to update a remote branch even when the local
	// branch does not descend from it.
	Force bool
	// InsecureSkipTLS skips ssl verify if protocol is https
	InsecureSkipTLS bool
	// CABundle specify additional ca bundle with system cert pool
	CABundle []byte
	// RequireRemoteRefs only allows a remote ref to be updated if its current
	// value is the one specified here.
	RequireRemoteRefs []config.RefSpec
	// FollowTags will send any annotated tags with a commit target reachable from
	// the refs already being pushed
	FollowTags bool
	// ForceWithLease allows a force push as long as the remote ref adheres to a "lease"
	ForceWithLease *ForceWithLease
	// PushOptions sets options to be transferred to the server during push.
	Options map[string]string
	// Atomic sets option to be an atomic push
	Atomic bool
	// ProxyOptions provides info required for connecting to a proxy.
	ProxyOptions transport.ProxyOptions
}

// ForceWithLease sets fields on the lease
// If neither RefName nor Hash are set, ForceWithLease protects
// all refs in the refspec by ensuring the ref of the remote in the local repsitory
// matches the one in the ref advertisement.
type ForceWithLease struct {
	// RefName, when set will protect the ref by ensuring it matches the
	// hash in the ref advertisement.
	RefName plumbing.ReferenceName
	// Hash is the expected object id of RefName. The push will be rejected unless this
	// matches the corresponding object id of RefName in the refs advertisement.
	Hash plumbing.Hash
}

// Validate validates the fields and sets the default values.
func (o *PushOptions) Validate() error {
	if o.RemoteName == "" {
		o.RemoteName = DefaultRemoteName
	}

	if len(o.RefSpecs) == 0 {
		o.RefSpecs = []config.RefSpec{
			config.RefSpec(config.DefaultPushRefSpec),
		}
	}

	for _, r := range o.RefSpecs {
		if err := r.Validate(); err != nil {
			return err
		}
	}

	return nil
}

// SubmoduleUpdateOptions describes how a submodule update should be performed.
type SubmoduleUpdateOptions struct {
	// Init, if true initializes the submodules recorded in the index.
	Init bool
	// NoFetch tell to the update command to not fetch new objects from the
	// remote site.
	NoFetch bool
	// RecurseSubmodules the update is performed not only in the submodules of
	// the current repository but also in any nested submodules inside those
	// submodules (and so on). Until the SubmoduleRescursivity is reached.
	RecurseSubmodules SubmoduleRescursivity
	// Auth credentials, if required, to use with the remote repository.
	Auth transport.AuthMethod
}

var (
	ErrBranchHashExclusive  = errors.New("Branch and Hash are mutually exclusive")
	ErrCreateRequiresBranch = errors.New("Branch is mandatory when Create is used")
)

// CheckoutOptions describes how a checkout operation should be performed.
type CheckoutOptions struct {
	// Hash is the hash of the commit to be checked out. If used, HEAD will be
	// in detached mode. If Create is not used, Branch and Hash are mutually
	// exclusive.
	Hash plumbing.Hash
	// Branch to be checked out, if Branch and Hash are empty is set to `master`.
	Branch plumbing.ReferenceName
	// Create a new branch named Branch and start it at Hash.
	Create bool
	// Force, if true when switching branches, proceed even if the index or the
	// working tree differs from HEAD. This is used to throw away local changes
	Force bool
	// Keep, if true when switching branches, local changes (the index or the
	// working tree changes) will be kept so that they can be committed to the
	// target branch. Force and Keep are mutually exclusive, should not be both
	// set to true.
	Keep bool
	// SparseCheckoutDirectories
	SparseCheckoutDirectories []string
}

// Validate validates the fields and sets the default values.
func (o *CheckoutOptions) Validate() error {
	if !o.Create && !o.Hash.IsZero() && o.Branch != "" {
		return ErrBranchHashExclusive
	}

	if o.Create && o.Branch == "" {
		return ErrCreateRequiresBranch
	}

	if o.Branch == "" {
		o.Branch = plumbing.Master
	}

	return nil
}

// ResetMode defines the mode of a reset operation.
type ResetMode int8

const (
	// MixedReset resets the index but not the working tree (i.e., the changed
	// files are preserved but not marked for commit) and reports what has not
	// been updated. This is the default action.
	MixedReset ResetMode = iota
	// HardReset resets the index and working tree. Any changes to tracked files
	// in the working tree are discarded.
	HardReset
	// MergeReset resets the index and updates the files in the working tree
	// that are different between Commit and HEAD, but keeps those which are
	// different between the index and working tree (i.e. which have changes
	// which have not been added).
	//
	// If a file that is different between Commit and the index has unstaged
	// changes, reset is aborted.
	MergeReset
	// SoftReset does not touch the index file or the working tree at all (but
	// resets the head to <commit>, just like all modes do). This leaves all
	// your changed files "Changes to be committed", as git status would put it.
	SoftReset
)

// ResetOptions describes how a reset operation should be performed.
type ResetOptions struct {
	// Commit, if commit is present set the current branch head (HEAD) to it.
	Commit plumbing.Hash
	// Mode, form resets the current branch head to Commit and possibly updates
	// the index (resetting it to the tree of Commit) and the working tree
	// depending on Mode. If empty MixedReset is used.
	Mode ResetMode
}

// Validate validates the fields and sets the default values.
func (o *ResetOptions) Validate(r *Repository) error {
	if o.Commit == plumbing.ZeroHash {
		ref, err := r.Head()
		if err != nil {
			return err
		}

		o.Commit = ref.Hash()
	}

	return nil
}

type LogOrder int8

const (
	LogOrderDefault LogOrder = iota
	LogOrderDFS
	LogOrderDFSPost
	LogOrderBSF
	LogOrderCommitterTime
)

// LogOptions describes how a log action should be performed.
type LogOptions struct {
	// When the From option is set the log will only contain commits
	// reachable from it. If this option is not set, HEAD will be used as
	// the default From.
	From plumbing.Hash

	// The default traversal algorithm is Depth-first search
	// set Order=LogOrderCommitterTime for ordering by committer time (more compatible with `git log`)
	// set Order=LogOrderBSF for Breadth-first search
	Order LogOrder

	// Show only those commits in which the specified file was inserted/updated.
	// It is equivalent to running `git log -- <file-name>`.
	// this field is kept for compatibility, it can be replaced with PathFilter
	FileName *string

	// Filter commits based on the path of files that are updated
	// takes file path as argument and should return true if the file is desired
	// It can be used to implement `git log -- <path>`
	// either <path> is a file path, or directory path, or a regexp of file/directory path
	PathFilter func(string) bool

	// Pretend as if all the refs in refs/, along with HEAD, are listed on the command line as <commit>.
	// It is equivalent to running `git log --all`.
	// If set on true, the From option will be ignored.
	All bool

	// Show commits more recent than a specific date.
	// It is equivalent to running `git log --since <date>` or `git log --after <date>`.
	Since *time.Time

	// Show commits older than a specific date.
	// It is equivalent to running `git log --until <date>` or `git log --before <date>`.
	Until *time.Time
}

var (
	ErrMissingAuthor = errors.New("author field is required")
)

// AddOptions describes how an `add` operation should be performed
type AddOptions struct {
	// All equivalent to `git add -A`, update the index not only where the
	// working tree has a file matching `Path` but also where the index already
	// has an entry. This adds, modifies, and removes index entries to match the
	// working tree.  If no `Path` nor `Glob` is given when `All` option is
	// used, all files in the entire working tree are updated.
	All bool
	// Path is the exact filepath to the file or directory to be added.
	Path string
	// Glob adds all paths, matching pattern, to the index. If pattern matches a
	// directory path, all directory contents are added to the index recursively.
	Glob string
}

// Validate validates the fields and sets the default values.
func (o *AddOptions) Validate(r *Repository) error {
	if o.Path != "" && o.Glob != "" {
		return fmt.Errorf("fields Path and Glob are mutual exclusive")
	}

	return nil
}

// CommitOptions describes how a commit operation should be performed.
type CommitOptions struct {
	// All automatically stage files that have been modified and deleted, but
	// new files you have not told Git about are not affected.
	All bool
	// AllowEmptyCommits enable empty commits to be created. An empty commit
	// is when no changes to the tree were made, but a new commit message is
	// provided. The default behavior is false, which results in ErrEmptyCommit.
	AllowEmptyCommits bool
	// Author is the author's signature of the commit. If Author is empty the
	// Name and Email is read from the config, and time.Now it's used as When.
	Author *object.Signature
	// Committer is the committer's signature of the commit. If Committer is
	// nil the Author signature is used.
	Committer *object.Signature
	// Parents are the parents commits for the new commit, by default when
	// len(Parents) is zero, the hash of HEAD reference is used.
	Parents []plumbing.Hash
	// SignKey denotes a key to sign the commit with. A nil value here means the
	// commit will not be signed. The private key must be present and already
	// decrypted.
	SignKey *openpgp.Entity
}

// Validate validates the fields and sets the default values.
func (o *CommitOptions) Validate(r *Repository) error {
	if o.Author == nil {
		if err := o.loadConfigAuthorAndCommitter(r); err != nil {
			return err
		}
	}

	if o.Committer == nil {
		o.Committer = o.Author
	}

	if len(o.Parents) == 0 {
		head, err := r.Head()
		if err != nil && err != plumbing.ErrReferenceNotFound {
			return err
		}

		if head != nil {
			o.Parents = []plumbing.Hash{head.Hash()}
		}
	}

	return nil
}

func (o *CommitOptions) loadConfigAuthorAndCommitter(r *Repository) error {
	cfg, err := r.ConfigScoped(config.SystemScope)
	if err != nil {
		return err
	}

	if o.Author == nil && cfg.Author.Email != "" && cfg.Author.Name != "" {
		o.Author = &object.Signature{
			Name:  cfg.Author.Name,
			Email: cfg.Author.Email,
			When:  time.Now(),
		}
	}

	if o.Committer == nil && cfg.Committer.Email != "" && cfg.Committer.Name != "" {
		o.Committer = &object.Signature{
			Name:  cfg.Committer.Name,
			Email: cfg.Committer.Email,
			When:  time.Now(),
		}
	}

	if o.Author == nil && cfg.User.Email != "" && cfg.User.Name != "" {
		o.Author = &object.Signature{
			Name:  cfg.User.Name,
			Email: cfg.User.Email,
			When:  time.Now(),
		}
	}

	if o.Author == nil {
		return ErrMissingAuthor
	}

	return nil
}

var (
	ErrMissingName    = errors.New("name field is required")
	ErrMissingTagger  = errors.New("tagger field is required")
	ErrMissingMessage = errors.New("message field is required")
)

// CreateTagOptions describes how a tag object should be created.
type CreateTagOptions struct {
	// Tagger defines the signature of the tag creator. If Tagger is empty the
	// Name and Email is read from the config, and time.Now it's used as When.
	Tagger *object.Signature
	// Message defines the annotation of the tag. It is canonicalized during
	// validation into the format expected by git - no leading whitespace and
	// ending in a newline.
	Message string
	// SignKey denotes a key to sign the tag with. A nil value here means the tag
	// will not be signed. The private key must be present and already decrypted.
	SignKey *openpgp.Entity
}

// Validate validates the fields and sets the default values.
func (o *CreateTagOptions) Validate(r *Repository, hash plumbing.Hash) error {
	if o.Tagger == nil {
		if err := o.loadConfigTagger(r); err != nil {
			return err
		}
	}

	if o.Message == "" {
		return ErrMissingMessage
	}

	// Canonicalize the message into the expected message format.
	o.Message = strings.TrimSpace(o.Message) + "\n"

	return nil
}

func (o *CreateTagOptions) loadConfigTagger(r *Repository) error {
	cfg, err := r.ConfigScoped(config.SystemScope)
	if err != nil {
		return err
	}

	if o.Tagger == nil && cfg.Author.Email != "" && cfg.Author.Name != "" {
		o.Tagger = &object.Signature{
			Name:  cfg.Author.Name,
			Email: cfg.Author.Email,
			When:  time.Now(),
		}
	}

	if o.Tagger == nil && cfg.User.Email != "" && cfg.User.Name != "" {
		o.Tagger = &object.Signature{
			Name:  cfg.User.Name,
			Email: cfg.User.Email,
			When:  time.Now(),
		}
	}

	if o.Tagger == nil {
		return ErrMissingTagger
	}

	return nil
}

// ListOptions describes how a remote list should be performed.
type ListOptions struct {
	// Auth credentials, if required, to use with the remote repository.
	Auth transport.AuthMethod
	// InsecureSkipTLS skips ssl verify if protocol is https
	InsecureSkipTLS bool
	// CABundle specify additional ca bundle with system cert pool
	CABundle []byte
	// PeelingOption defines how peeled objects are handled during a
	// remote list.
	PeelingOption PeelingOption
	// ProxyOptions provides info required for connecting to a proxy.
	ProxyOptions transport.ProxyOptions
}

// PeelingOption represents the different ways to handle peeled references.
//
// Peeled references represent the underlying object of an annotated
// (or signed) tag. Refer to upstream documentation for more info:
// https://github.com/git/git/blob/master/Documentation/technical/reftable.txt
type PeelingOption uint8

const (
	// IgnorePeeled ignores all peeled reference names. This is the default behavior.
	IgnorePeeled PeelingOption = 0
	// OnlyPeeled returns only peeled reference names.
	OnlyPeeled PeelingOption = 1
	// AppendPeeled appends peeled reference names to the reference list.
	AppendPeeled PeelingOption = 2
)

// CleanOptions describes how a clean should be performed.
type CleanOptions struct {
	Dir bool
}

// GrepOptions describes how a grep should be performed.
type GrepOptions struct {
	// Patterns are compiled Regexp objects to be matched.
	Patterns []*regexp.Regexp
	// InvertMatch selects non-matching lines.
	InvertMatch bool
	// CommitHash is the hash of the commit from which worktree should be derived.
	CommitHash plumbing.Hash
	// ReferenceName is the branch or tag name from which worktree should be derived.
	ReferenceName plumbing.ReferenceName
	// PathSpecs are compiled Regexp objects of pathspec to use in the matching.
	PathSpecs []*regexp.Regexp
}

var (
	ErrHashOrReference = errors.New("ambiguous options, only one of CommitHash or ReferenceName can be passed")
)

// Validate validates the fields and sets the default values.
//
// TODO: deprecate in favor of Validate(r *Repository) in v6.
func (o *GrepOptions) Validate(w *Worktree) error {
	return o.validate(w.r)
}

func (o *GrepOptions) validate(r *Repository) error {
	if !o.CommitHash.IsZero() && o.ReferenceName != "" {
		return ErrHashOrReference
	}

	// If none of CommitHash and ReferenceName are provided, set commit hash of
	// the repository's head.
	if o.CommitHash.IsZero() && o.ReferenceName == "" {
		ref, err := r.Head()
		if err != nil {
			return err
		}
		o.CommitHash = ref.Hash()
	}

	return nil
}

// PlainOpenOptions describes how opening a plain repository should be
// performed.
type PlainOpenOptions struct {
	// DetectDotGit defines whether parent directories should be
	// walked until a .git directory or file is found.
	DetectDotGit bool
	// Enable .git/commondir support (see https://git-scm.com/docs/gitrepository-layout#Documentation/gitrepository-layout.txt).
	// NOTE: This option will only work with the filesystem storage.
	EnableDotGitCommonDir bool
}

// Validate validates the fields and sets the default values.
func (o *PlainOpenOptions) Validate() error { return nil }

type PlainInitOptions struct {
	ObjectFormat formatcfg.ObjectFormat
}

// Validate validates the fields and sets the default values.
func (o *PlainInitOptions) Validate() error { return nil }