refactor!: return sets of git type flags instead of integers

This commit is contained in:
Aleksey Kulikov 2021-09-10 20:22:02 +03:00
parent 050c0eb57a
commit 7618f944c0
12 changed files with 459 additions and 191 deletions

View file

@ -1,121 +1,173 @@
/// Basic type of any Git reference. /// Basic type of any Git reference.
class ReferenceType { class ReferenceType {
const ReferenceType._(this._value); const ReferenceType._(this._value, this._name);
final int _value; final int _value;
final String _name;
/// Invalid reference. /// Invalid reference.
static const invalid = ReferenceType._(0); static const invalid = ReferenceType._(0, 'invalid');
/// A reference that points at an object id. /// A reference that points at an object id.
static const direct = ReferenceType._(1); static const direct = ReferenceType._(1, 'direct');
/// A reference that points at another reference. /// A reference that points at another reference.
static const symbolic = ReferenceType._(2); static const symbolic = ReferenceType._(2, 'symbolic');
static const all = ReferenceType._(3); static const all = ReferenceType._(3, 'all');
static const List<ReferenceType> values = [invalid, direct, symbolic, all];
int get value => _value; int get value => _value;
@override
String toString() => 'ReferenceType.$_name';
} }
/// Valid modes for index and tree entries. /// Valid modes for index and tree entries.
class GitFilemode { class GitFilemode {
const GitFilemode._(this._value); const GitFilemode._(this._value, this._name);
final int _value; final int _value;
final String _name;
static const unreadable = GitFilemode._(0); static const unreadable = GitFilemode._(0, 'unreadable');
static const tree = GitFilemode._(16384); static const tree = GitFilemode._(16384, 'tree');
static const blob = GitFilemode._(33188); static const blob = GitFilemode._(33188, 'blob');
static const blobExecutable = GitFilemode._(33261); static const blobExecutable = GitFilemode._(33261, 'blobExecutable');
static const link = GitFilemode._(40960); static const link = GitFilemode._(40960, 'link');
static const commit = GitFilemode._(57344); static const commit = GitFilemode._(57344, 'commit');
static const List<GitFilemode> values = [
unreadable,
tree,
blob,
blobExecutable,
link,
commit,
];
int get value => _value; int get value => _value;
@override
String toString() => 'GitFilemode.$_name';
} }
/// Flags to specify the sorting which a revwalk should perform. /// Flags to specify the sorting which a revwalk should perform.
class GitSort { class GitSort {
const GitSort._(this._value); const GitSort._(this._value, this._name);
final int _value; final int _value;
final String _name;
/// Sort the output with the same default method from `git`: reverse /// Sort the output with the same default method from `git`: reverse
/// chronological order. This is the default sorting for new walkers. /// chronological order. This is the default sorting for new walkers.
static const none = GitSort._(0); static const none = GitSort._(0, 'none');
/// Sort the repository contents in topological order (no parents before /// Sort the repository contents in topological order (no parents before
/// all of its children are shown); this sorting mode can be combined /// all of its children are shown); this sorting mode can be combined
/// with time sorting to produce `git`'s `--date-order``. /// with time sorting to produce `git`'s `--date-order``.
static const topological = GitSort._(1); static const topological = GitSort._(1, 'topological');
/// Sort the repository contents by commit time; /// Sort the repository contents by commit time;
/// this sorting mode can be combined with topological sorting. /// this sorting mode can be combined with topological sorting.
static const time = GitSort._(2); static const time = GitSort._(2, 'time');
/// Iterate through the repository contents in reverse order; this sorting mode /// Iterate through the repository contents in reverse order; this sorting mode
/// can be combined with any of the above. /// can be combined with any of the above.
static const reverse = GitSort._(4); static const reverse = GitSort._(4, 'reverse');
static const List<GitSort> values = [none, topological, time, reverse];
int get value => _value; int get value => _value;
@override
String toString() => 'GitSort.$_name';
} }
/// Basic type (loose or packed) of any Git object. /// Basic type (loose or packed) of any Git object.
class GitObject { class GitObject {
const GitObject._(this._value); const GitObject._(this._value, this._name);
final int _value; final int _value;
final String _name;
/// Object can be any of the following. /// Object can be any of the following.
static const any = GitObject._(-2); static const any = GitObject._(-2, 'any');
/// Object is invalid. /// Object is invalid.
static const invalid = GitObject._(-1); static const invalid = GitObject._(-1, 'invalid');
/// A commit object. /// A commit object.
static const commit = GitObject._(1); static const commit = GitObject._(1, 'commit');
/// A tree (directory listing) object. /// A tree (directory listing) object.
static const tree = GitObject._(2); static const tree = GitObject._(2, 'tree');
/// A file revision object. /// A file revision object.
static const blob = GitObject._(3); static const blob = GitObject._(3, 'blob');
/// An annotated tag object. /// An annotated tag object.
static const tag = GitObject._(4); static const tag = GitObject._(4, 'tag');
/// A delta, base is given by an offset. /// A delta, base is given by an offset.
static const offsetDelta = GitObject._(6); static const offsetDelta = GitObject._(6, 'offsetDelta');
/// A delta, base is given by object id. /// A delta, base is given by object id.
static const refDelta = GitObject._(7); static const refDelta = GitObject._(7, 'refDelta');
static const List<GitObject> values = [
any,
invalid,
commit,
tree,
blob,
tag,
offsetDelta,
refDelta,
];
int get value => _value; int get value => _value;
@override
String toString() => 'GitObject.$_name';
} }
/// Revparse flags, indicate the intended behavior of the spec. /// Revparse flags, indicate the intended behavior of the spec.
class GitRevParse { class GitRevParse {
const GitRevParse._(this._value); const GitRevParse._(this._value, this._name);
final int _value; final int _value;
final String _name;
/// The spec targeted a single object. /// The spec targeted a single object.
static const single = GitRevParse._(1); static const single = GitRevParse._(1, 'single');
/// The spec targeted a range of commits. /// The spec targeted a range of commits.
static const range = GitRevParse._(2); static const range = GitRevParse._(2, 'range');
/// The spec used the '...' operator, which invokes special semantics. /// The spec used the '...' operator, which invokes special semantics.
static const mergeBase = GitRevParse._(4); static const mergeBase = GitRevParse._(4, 'mergeBase');
static const List<GitRevParse> values = [single, range, mergeBase];
int get value => _value; int get value => _value;
@override
String toString() => 'GitRevParse.$_name';
} }
/// Basic type of any Git branch. /// Basic type of any Git branch.
class GitBranch { class GitBranch {
const GitBranch._(this._value); const GitBranch._(this._value, this._name);
final int _value; final int _value;
final String _name;
static const local = GitBranch._(1); static const local = GitBranch._(1, 'local');
static const remote = GitBranch._(2); static const remote = GitBranch._(2, 'remote');
static const all = GitBranch._(3); static const all = GitBranch._(3, 'all');
static const List<GitBranch> values = [local, remote, all];
int get value => _value; int get value => _value;
@override
String toString() => 'GitBranch.$_name';
} }
/// Status flags for a single file. /// Status flags for a single file.
@ -127,73 +179,115 @@ class GitBranch {
/// `GitStatus.wt` set of flags represent the status of the file in the /// `GitStatus.wt` set of flags represent the status of the file in the
/// working directory relative to the index. /// working directory relative to the index.
class GitStatus { class GitStatus {
const GitStatus._(this._value); const GitStatus._(this._value, this._name);
final int _value; final int _value;
final String _name;
static const current = GitStatus._(0); static const current = GitStatus._(0, 'current');
static const indexNew = GitStatus._(1); static const indexNew = GitStatus._(1, 'indexNew');
static const indexModified = GitStatus._(2); static const indexModified = GitStatus._(2, 'indexModified');
static const indexDeleted = GitStatus._(4); static const indexDeleted = GitStatus._(4, 'indexDeleted');
static const indexRenamed = GitStatus._(8); static const indexRenamed = GitStatus._(8, 'indexRenamed');
static const indexTypeChange = GitStatus._(16); static const indexTypeChange = GitStatus._(16, 'indexTypeChange');
static const wtNew = GitStatus._(128); static const wtNew = GitStatus._(128, 'wtNew');
static const wtModified = GitStatus._(256); static const wtModified = GitStatus._(256, 'wtModified');
static const wtDeleted = GitStatus._(512); static const wtDeleted = GitStatus._(512, 'wtDeleted');
static const wtTypeChange = GitStatus._(1024); static const wtTypeChange = GitStatus._(1024, 'wtTypeChange');
static const wtRenamed = GitStatus._(2048); static const wtRenamed = GitStatus._(2048, 'wtRenamed');
static const wtUnreadable = GitStatus._(4096); static const wtUnreadable = GitStatus._(4096, 'wtUnreadable');
static const ignored = GitStatus._(16384); static const ignored = GitStatus._(16384, 'ignored');
static const conflicted = GitStatus._(32768); static const conflicted = GitStatus._(32768, 'conflicted');
static const List<GitStatus> values = [
current,
indexNew,
indexModified,
indexDeleted,
indexRenamed,
indexTypeChange,
wtNew,
wtModified,
wtDeleted,
wtTypeChange,
wtRenamed,
wtUnreadable,
ignored,
conflicted,
];
int get value => _value; int get value => _value;
@override
String toString() => 'GitStatus.$_name';
} }
/// The results of `mergeAnalysis` indicate the merge opportunities. /// The results of `mergeAnalysis` indicate the merge opportunities.
class GitMergeAnalysis { class GitMergeAnalysis {
const GitMergeAnalysis._(this._value); const GitMergeAnalysis._(this._value, this._name);
final int _value; final int _value;
final String _name;
/// No merge is possible (unused). /// No merge is possible (unused).
static const none = GitMergeAnalysis._(0); static const none = GitMergeAnalysis._(0, 'none');
/// A "normal" merge; both HEAD and the given merge input have diverged /// A "normal" merge; both HEAD and the given merge input have diverged
/// from their common ancestor. The divergent commits must be merged. /// from their common ancestor. The divergent commits must be merged.
static const normal = GitMergeAnalysis._(1); static const normal = GitMergeAnalysis._(1, 'normal');
/// All given merge inputs are reachable from HEAD, meaning the /// All given merge inputs are reachable from HEAD, meaning the
/// repository is up-to-date and no merge needs to be performed. /// repository is up-to-date and no merge needs to be performed.
static const upToDate = GitMergeAnalysis._(2); static const upToDate = GitMergeAnalysis._(2, 'upToDate');
/// The given merge input is a fast-forward from HEAD and no merge /// The given merge input is a fast-forward from HEAD and no merge
/// needs to be performed. Instead, the client can check out the /// needs to be performed. Instead, the client can check out the
/// given merge input. /// given merge input.
static const fastForward = GitMergeAnalysis._(4); static const fastForward = GitMergeAnalysis._(4, 'fastForward');
/// The HEAD of the current repository is "unborn" and does not point to /// The HEAD of the current repository is "unborn" and does not point to
/// a valid commit. No merge can be performed, but the caller may wish /// a valid commit. No merge can be performed, but the caller may wish
/// to simply set HEAD to the target commit(s). /// to simply set HEAD to the target commit(s).
static const unborn = GitMergeAnalysis._(8); static const unborn = GitMergeAnalysis._(8, 'unborn');
static const List<GitMergeAnalysis> values = [
normal,
upToDate,
fastForward,
unborn,
];
int get value => _value; int get value => _value;
@override
String toString() => 'GitMergeAnalysis.$_name';
} }
/// The user's stated preference for merges. /// The user's stated preference for merges.
class GitMergePreference { class GitMergePreference {
const GitMergePreference._(this._value); const GitMergePreference._(this._value, this._name);
final int _value; final int _value;
final String _name;
/// No configuration was found that suggests a preferred behavior for merge. /// No configuration was found that suggests a preferred behavior for merge.
static const none = GitMergePreference._(0); static const none = GitMergePreference._(0, 'none');
/// There is a `merge.ff=false` configuration setting, suggesting that /// There is a `merge.ff=false` configuration setting, suggesting that
/// the user does not want to allow a fast-forward merge. /// the user does not want to allow a fast-forward merge.
static const noFastForward = GitMergePreference._(1); static const noFastForward = GitMergePreference._(1, 'noFastForward');
/// There is a `merge.ff=only` configuration setting, suggesting that /// There is a `merge.ff=only` configuration setting, suggesting that
/// the user only wants fast-forward merges. /// the user only wants fast-forward merges.
static const fastForwardOnly = GitMergePreference._(2); static const fastForwardOnly = GitMergePreference._(2, 'fastForwardOnly');
static const List<GitMergePreference> values = [
none,
noFastForward,
fastForwardOnly,
];
int get value => _value; int get value => _value;
@override
String toString() => 'GitMergePreference.$_name';
} }
/// Repository state. /// Repository state.
@ -201,113 +295,174 @@ class GitMergePreference {
/// These values represent possible states for the repository to be in, /// These values represent possible states for the repository to be in,
/// based on the current operation which is ongoing. /// based on the current operation which is ongoing.
class GitRepositoryState { class GitRepositoryState {
const GitRepositoryState._(this._value); const GitRepositoryState._(this._value, this._name);
final int _value; final int _value;
final String _name;
static const none = GitRepositoryState._(0); static const none = GitRepositoryState._(0, 'none');
static const merge = GitRepositoryState._(1); static const merge = GitRepositoryState._(1, 'merge');
static const revert = GitRepositoryState._(2); static const revert = GitRepositoryState._(2, 'revert');
static const reverSequence = GitRepositoryState._(3); static const revertSequence = GitRepositoryState._(3, 'revertSequence');
static const cherrypick = GitRepositoryState._(4); static const cherrypick = GitRepositoryState._(4, 'cherrypick');
static const cherrypickSequence = GitRepositoryState._(5); static const cherrypickSequence =
static const bisect = GitRepositoryState._(6); GitRepositoryState._(5, 'cherrypickSequence');
static const rebase = GitRepositoryState._(7); static const bisect = GitRepositoryState._(6, 'bisect');
static const rebaseInteractive = GitRepositoryState._(8); static const rebase = GitRepositoryState._(7, 'rebase');
static const rebaseMerge = GitRepositoryState._(9); static const rebaseInteractive = GitRepositoryState._(8, 'rebaseInteractive');
static const applyMailbox = GitRepositoryState._(10); static const rebaseMerge = GitRepositoryState._(9, 'rebaseMerge');
static const applyMailboxOrRebase = GitRepositoryState._(11); static const applyMailbox = GitRepositoryState._(10, 'applyMailbox');
static const applyMailboxOrRebase =
GitRepositoryState._(11, 'applyMailboxOrRebase');
static const List<GitRepositoryState> values = [
none,
merge,
revert,
revertSequence,
cherrypick,
cherrypickSequence,
bisect,
rebase,
rebaseInteractive,
rebaseMerge,
applyMailbox,
applyMailboxOrRebase,
];
int get value => _value; int get value => _value;
@override
String toString() => 'GitRepositoryState.$_name';
} }
/// Flags for merge options. /// Flags for merge options.
class GitMergeFlag { class GitMergeFlag {
const GitMergeFlag._(this._value); const GitMergeFlag._(this._value, this._name);
final int _value; final int _value;
final String _name;
/// Detect renames that occur between the common ancestor and the "ours" /// Detect renames that occur between the common ancestor and the "ours"
/// side or the common ancestor and the "theirs" side. This will enable /// side or the common ancestor and the "theirs" side. This will enable
/// the ability to merge between a modified and renamed file. /// the ability to merge between a modified and renamed file.
static const findRenames = GitMergeFlag._(1); static const findRenames = GitMergeFlag._(1, 'findRenames');
/// If a conflict occurs, exit immediately instead of attempting to /// If a conflict occurs, exit immediately instead of attempting to
/// continue resolving conflicts. The merge operation will fail with /// continue resolving conflicts. The merge operation will fail with
/// and no index will be returned. /// and no index will be returned.
static const failOnConflict = GitMergeFlag._(2); static const failOnConflict = GitMergeFlag._(2, 'failOnConflict');
/// Do not write the REUC extension on the generated index. /// Do not write the REUC extension on the generated index.
static const skipREUC = GitMergeFlag._(4); static const skipREUC = GitMergeFlag._(4, 'skipREUC');
/// If the commits being merged have multiple merge bases, do not build /// If the commits being merged have multiple merge bases, do not build
/// a recursive merge base (by merging the multiple merge bases), /// a recursive merge base (by merging the multiple merge bases),
/// instead simply use the first base. /// instead simply use the first base.
static const noRecursive = GitMergeFlag._(8); static const noRecursive = GitMergeFlag._(8, 'noRecursive');
static const List<GitMergeFlag> values = [
findRenames,
failOnConflict,
skipREUC,
noRecursive,
];
int get value => _value; int get value => _value;
@override
String toString() => 'GitMergeFlag.$_name';
} }
/// Merge file favor options to instruct the file-level merging functionality /// Merge file favor options to instruct the file-level merging functionality
/// on how to deal with conflicting regions of the files. /// on how to deal with conflicting regions of the files.
class GitMergeFileFavor { class GitMergeFileFavor {
const GitMergeFileFavor._(this._value); const GitMergeFileFavor._(this._value, this._name);
final int _value; final int _value;
final String _name;
/// When a region of a file is changed in both branches, a conflict /// When a region of a file is changed in both branches, a conflict
/// will be recorded in the index. This is the default. /// will be recorded in the index. This is the default.
static const normal = GitMergeFileFavor._(0); static const normal = GitMergeFileFavor._(0, 'normal');
/// When a region of a file is changed in both branches, the file /// When a region of a file is changed in both branches, the file
/// created in the index will contain the "ours" side of any conflicting /// created in the index will contain the "ours" side of any conflicting
/// region. The index will not record a conflict. /// region. The index will not record a conflict.
static const ours = GitMergeFileFavor._(1); static const ours = GitMergeFileFavor._(1, 'ours');
/// When a region of a file is changed in both branches, the file /// When a region of a file is changed in both branches, the file
/// created in the index will contain the "theirs" side of any conflicting /// created in the index will contain the "theirs" side of any conflicting
/// region. The index will not record a conflict. /// region. The index will not record a conflict.
static const theirs = GitMergeFileFavor._(2); static const theirs = GitMergeFileFavor._(2, 'theirs');
/// When a region of a file is changed in both branches, the file /// When a region of a file is changed in both branches, the file
/// created in the index will contain each unique line from each side, /// created in the index will contain each unique line from each side,
/// which has the result of combining both files. The index will not /// which has the result of combining both files. The index will not
/// record a conflict. /// record a conflict.
static const union = GitMergeFileFavor._(3); static const union = GitMergeFileFavor._(3, 'union');
static const List<GitMergeFileFavor> values = [
normal,
ours,
theirs,
union,
];
int get value => _value; int get value => _value;
@override
String toString() => 'GitMergeFileFavor.$_name';
} }
/// File merging flags. /// File merging flags.
class GitMergeFileFlag { class GitMergeFileFlag {
const GitMergeFileFlag._(this._value); const GitMergeFileFlag._(this._value, this._name);
final int _value; final int _value;
final String _name;
/// Defaults. /// Defaults.
static const defaults = GitMergeFileFlag._(0); static const defaults = GitMergeFileFlag._(0, 'defaults');
/// Create standard conflicted merge files. /// Create standard conflicted merge files.
static const styleMerge = GitMergeFileFlag._(1); static const styleMerge = GitMergeFileFlag._(1, 'styleMerge');
/// Create diff3-style files. /// Create diff3-style files.
static const styleDiff3 = GitMergeFileFlag._(2); static const styleDiff3 = GitMergeFileFlag._(2, 'styleDiff3');
/// Condense non-alphanumeric regions for simplified diff file. /// Condense non-alphanumeric regions for simplified diff file.
static const simplifyAlnum = GitMergeFileFlag._(4); static const simplifyAlnum = GitMergeFileFlag._(4, 'simplifyAlnum');
/// Ignore all whitespace. /// Ignore all whitespace.
static const ignoreWhitespace = GitMergeFileFlag._(8); static const ignoreWhitespace = GitMergeFileFlag._(8, 'ignoreWhitespace');
/// Ignore changes in amount of whitespace. /// Ignore changes in amount of whitespace.
static const ignoreWhitespaceChange = GitMergeFileFlag._(16); static const ignoreWhitespaceChange =
GitMergeFileFlag._(16, 'ignoreWhitespaceChange');
/// Ignore whitespace at end of line. /// Ignore whitespace at end of line.
static const ignoreWhitespaceEOL = GitMergeFileFlag._(32); static const ignoreWhitespaceEOL =
GitMergeFileFlag._(32, 'ignoreWhitespaceEOL');
/// Use the "patience diff" algorithm. /// Use the "patience diff" algorithm.
static const diffPatience = GitMergeFileFlag._(64); static const diffPatience = GitMergeFileFlag._(64, 'diffPatience');
/// Take extra time to find minimal diff. /// Take extra time to find minimal diff.
static const diffMinimal = GitMergeFileFlag._(128); static const diffMinimal = GitMergeFileFlag._(128, 'diffMinimal');
static const List<GitMergeFileFlag> values = [
defaults,
styleMerge,
styleDiff3,
simplifyAlnum,
ignoreWhitespace,
ignoreWhitespaceChange,
ignoreWhitespaceEOL,
diffPatience,
diffMinimal,
];
int get value => _value; int get value => _value;
@override
String toString() => 'GitMergeFileFlag.$_name';
} }
/// Checkout behavior flags. /// Checkout behavior flags.
@ -316,11 +471,12 @@ class GitMergeFileFlag {
/// to match a target tree. Unlike git checkout, it does not move the HEAD /// to match a target tree. Unlike git checkout, it does not move the HEAD
/// commit for you - use `setHead` or the like to do that. /// commit for you - use `setHead` or the like to do that.
class GitCheckout { class GitCheckout {
const GitCheckout._(this._value); const GitCheckout._(this._value, this._name);
final int _value; final int _value;
final String _name;
/// Default is a dry run, no actual updates. /// Default is a dry run, no actual updates.
static const none = GitCheckout._(0); static const none = GitCheckout._(0, 'none');
/// Allow safe updates that cannot overwrite uncommitted data. /// Allow safe updates that cannot overwrite uncommitted data.
/// If the uncommitted changes don't conflict with the checked out files, /// If the uncommitted changes don't conflict with the checked out files,
@ -328,65 +484,97 @@ class GitCheckout {
/// ///
/// Mutually exclusive with [GitCheckout.force]. /// Mutually exclusive with [GitCheckout.force].
/// [GitCheckout.force] takes precedence over [GitCheckout.safe]. /// [GitCheckout.force] takes precedence over [GitCheckout.safe].
static const safe = GitCheckout._(1); static const safe = GitCheckout._(1, 'safe');
/// Allow all updates to force working directory to look like index. /// Allow all updates to force working directory to look like index.
/// ///
/// Mutually exclusive with [GitCheckout.safe]. /// Mutually exclusive with [GitCheckout.safe].
/// [GitCheckout.force] takes precedence over [GitCheckout.safe]. /// [GitCheckout.force] takes precedence over [GitCheckout.safe].
static const force = GitCheckout._(2); static const force = GitCheckout._(2, 'force');
/// Allow checkout to recreate missing files. /// Allow checkout to recreate missing files.
static const recreateMissing = GitCheckout._(4); static const recreateMissing = GitCheckout._(4, 'recreateMissing');
/// Allow checkout to make safe updates even if conflicts are found. /// Allow checkout to make safe updates even if conflicts are found.
static const allowConflicts = GitCheckout._(16); static const allowConflicts = GitCheckout._(16, 'allowConflicts');
/// Remove untracked files not in index (that are not ignored). /// Remove untracked files not in index (that are not ignored).
static const removeUntracked = GitCheckout._(32); static const removeUntracked = GitCheckout._(32, 'removeUntracked');
/// Remove ignored files not in index. /// Remove ignored files not in index.
static const removeIgnored = GitCheckout._(64); static const removeIgnored = GitCheckout._(64, 'removeIgnored');
/// Only update existing files, don't create new ones. /// Only update existing files, don't create new ones.
static const updateOnly = GitCheckout._(128); static const updateOnly = GitCheckout._(128, 'updateOnly');
/// Normally checkout updates index entries as it goes; this stops that. /// Normally checkout updates index entries as it goes; this stops that.
/// Implies [GitCheckout.dontWriteIndex]. /// Implies [GitCheckout.dontWriteIndex].
static const dontUpdateIndex = GitCheckout._(256); static const dontUpdateIndex = GitCheckout._(256, 'dontUpdateIndex');
/// Don't refresh index/config/etc before doing checkout. /// Don't refresh index/config/etc before doing checkout.
static const noRefresh = GitCheckout._(512); static const noRefresh = GitCheckout._(512, 'noRefresh');
/// Allow checkout to skip unmerged files. /// Allow checkout to skip unmerged files.
static const skipUnmerged = GitCheckout._(1024); static const skipUnmerged = GitCheckout._(1024, 'skipUnmerged');
/// For unmerged files, checkout stage 2 from index. /// For unmerged files, checkout stage 2 from index.
static const useOurs = GitCheckout._(2048); static const useOurs = GitCheckout._(2048, 'useOurs');
/// For unmerged files, checkout stage 3 from index. /// For unmerged files, checkout stage 3 from index.
static const useTheirs = GitCheckout._(4096); static const useTheirs = GitCheckout._(4096, 'useTheirs');
/// Treat pathspec as simple list of exact match file paths. /// Treat pathspec as simple list of exact match file paths.
static const disablePathspecMatch = GitCheckout._(8192); static const disablePathspecMatch =
GitCheckout._(8192, 'disablePathspecMatch');
/// Ignore directories in use, they will be left empty. /// Ignore directories in use, they will be left empty.
static const skipLockedDirectories = GitCheckout._(262144); static const skipLockedDirectories =
GitCheckout._(262144, 'skipLockedDirectories');
/// Don't overwrite ignored files that exist in the checkout target. /// Don't overwrite ignored files that exist in the checkout target.
static const dontOverwriteIgnored = GitCheckout._(524288); static const dontOverwriteIgnored =
GitCheckout._(524288, 'dontOverwriteIgnored');
/// Write normal merge files for conflicts. /// Write normal merge files for conflicts.
static const conflictStyleMerge = GitCheckout._(1048576); static const conflictStyleMerge =
GitCheckout._(1048576, 'conflictStyleMerge');
/// Include common ancestor data in diff3 format files for conflicts. /// Include common ancestor data in diff3 format files for conflicts.
static const conflictStyleDiff3 = GitCheckout._(2097152); static const conflictStyleDiff3 =
GitCheckout._(2097152, 'conflictStyleDiff3');
/// Don't overwrite existing files or folders. /// Don't overwrite existing files or folders.
static const dontRemoveExisting = GitCheckout._(4194304); static const dontRemoveExisting =
GitCheckout._(4194304, 'dontRemoveExisting');
/// Normally checkout writes the index upon completion; this prevents that. /// Normally checkout writes the index upon completion; this prevents that.
static const dontWriteIndex = GitCheckout._(8388608); static const dontWriteIndex = GitCheckout._(8388608, 'dontWriteIndex');
static const List<GitCheckout> values = [
none,
safe,
force,
recreateMissing,
allowConflicts,
removeUntracked,
removeIgnored,
updateOnly,
dontUpdateIndex,
noRefresh,
skipUnmerged,
useOurs,
useTheirs,
disablePathspecMatch,
skipLockedDirectories,
dontOverwriteIgnored,
conflictStyleMerge,
conflictStyleDiff3,
dontRemoveExisting,
dontWriteIndex,
];
int get value => _value; int get value => _value;
@override
String toString() => 'GitCheckout.$_name';
} }

View file

@ -208,7 +208,10 @@ class IndexEntry {
String get sha => _oidToHex(_indexEntryPointer.ref.id); String get sha => _oidToHex(_indexEntryPointer.ref.id);
/// Returns the UNIX file attributes of a index entry. /// Returns the UNIX file attributes of a index entry.
GitFilemode get mode => intToGitFilemode(_indexEntryPointer.ref.mode); GitFilemode get mode {
final modeInt = _indexEntryPointer.ref.mode;
return GitFilemode.values.singleWhere((mode) => modeInt == mode.value);
}
/// Sets the UNIX file attributes of a index entry. /// Sets the UNIX file attributes of a index entry.
set mode(GitFilemode mode) => _indexEntryPointer.ref.mode = mode.value; set mode(GitFilemode mode) => _indexEntryPointer.ref.mode = mode.value;

View file

@ -194,7 +194,11 @@ class Repository {
/// Returns the status of a git repository - ie, whether an operation /// Returns the status of a git repository - ie, whether an operation
/// (merge, cherry-pick, etc) is in progress. /// (merge, cherry-pick, etc) is in progress.
int get state => bindings.state(_repoPointer); GitRepositoryState get state {
final stateInt = bindings.state(_repoPointer);
return GitRepositoryState.values
.singleWhere((flag) => stateInt == flag.value);
}
/// Removes all the metadata associated with an ongoing command like /// Removes all the metadata associated with an ongoing command like
/// merge, revert, cherry-pick, etc. For example: MERGE_HEAD, MERGE_MSG, etc. /// merge, revert, cherry-pick, etc. For example: MERGE_HEAD, MERGE_MSG, etc.
@ -357,7 +361,7 @@ class Repository {
/// Returns the list of commits starting from provided [sha] hex string. /// Returns the list of commits starting from provided [sha] hex string.
/// ///
/// If [sorting] isn't provided default will be used (reverse chronological order, like in git). /// If [sorting] isn't provided default will be used (reverse chronological order, like in git).
List<Commit> log(String sha, [List<GitSort> sorting = const [GitSort.none]]) { List<Commit> log(String sha, [Set<GitSort> sorting = const {GitSort.none}]) {
final oid = Oid.fromSHA(this, sha); final oid = Oid.fromSHA(this, sha);
final walker = RevWalk(this); final walker = RevWalk(this);
@ -454,8 +458,8 @@ class Repository {
/// Checks status of the repository and returns map of file paths and their statuses. /// Checks status of the repository and returns map of file paths and their statuses.
/// ///
/// Returns empty map if there are no changes in statuses. /// Returns empty map if there are no changes in statuses.
Map<String, int> get status { Map<String, Set<GitStatus>> get status {
var result = <String, int>{}; var result = <String, Set<GitStatus>>{};
var list = status_bindings.listNew(_repoPointer); var list = status_bindings.listNew(_repoPointer);
var count = status_bindings.listEntryCount(list); var count = status_bindings.listEntryCount(list);
@ -471,7 +475,15 @@ class Repository {
.cast<Utf8>() .cast<Utf8>()
.toDartString(); .toDartString();
} }
result[path] = entry.ref.status; var statuses = <GitStatus>{};
// Skipping GitStatus.current because entry that is in the list can't be without changes
// but `&` on `0` value falsly adds it to the set of flags
for (var flag in GitStatus.values.skip(1)) {
if (entry.ref.status & flag.value == flag.value) {
statuses.add(flag);
}
}
result[path] = statuses;
} }
status_bindings.listFree(list); status_bindings.listFree(list);
@ -487,7 +499,23 @@ class Repository {
/// through looking for the path that you are interested in. /// through looking for the path that you are interested in.
/// ///
/// Throws a [LibGit2Error] if error occured. /// Throws a [LibGit2Error] if error occured.
int statusFile(String path) => status_bindings.file(_repoPointer, path); Set<GitStatus> statusFile(String path) {
final statusInt = status_bindings.file(_repoPointer, path);
var statuses = <GitStatus>{};
if (statusInt == GitStatus.current.value) {
statuses.add(GitStatus.current);
} else {
// Skipping GitStatus.current because `&` on `0` value falsly adds it to the set of flags
for (var flag in GitStatus.values.skip(1)) {
if (statusInt & flag.value == flag.value) {
statuses.add(flag);
}
}
}
return statuses;
}
/// Finds a merge base between two commits. /// Finds a merge base between two commits.
/// ///
@ -509,14 +537,30 @@ class Repository {
/// respectively. /// respectively.
/// ///
/// Throws a [LibGit2Error] if error occured. /// Throws a [LibGit2Error] if error occured.
List<int> mergeAnalysis(Oid theirHead, [String ourRef = 'HEAD']) { List<Set<dynamic>> mergeAnalysis(Oid theirHead, [String ourRef = 'HEAD']) {
final ref = references[ourRef]; final ref = references[ourRef];
final head = commit_bindings.annotatedLookup( final head = commit_bindings.annotatedLookup(
_repoPointer, _repoPointer,
theirHead.pointer, theirHead.pointer,
); );
final result = merge_bindings.analysis(_repoPointer, ref.pointer, head, 1); var result = <Set<dynamic>>[];
var analysisSet = <GitMergeAnalysis>{};
final analysisInt = merge_bindings.analysis(
_repoPointer,
ref.pointer,
head,
1,
);
for (var analysis in GitMergeAnalysis.values) {
if (analysisInt[0] & analysis.value == analysis.value) {
analysisSet.add(analysis);
}
}
result.add(analysisSet);
result.add(
{GitMergePreference.values.singleWhere((e) => analysisInt[1] == e.value)},
);
commit_bindings.annotatedFree(head.value); commit_bindings.annotatedFree(head.value);
ref.free(); ref.free();
@ -553,19 +597,15 @@ class Repository {
required Commit ourCommit, required Commit ourCommit,
required Commit theirCommit, required Commit theirCommit,
GitMergeFileFavor favor = GitMergeFileFavor.normal, GitMergeFileFavor favor = GitMergeFileFavor.normal,
List<GitMergeFlag> mergeFlags = const [GitMergeFlag.findRenames], Set<GitMergeFlag> mergeFlags = const {GitMergeFlag.findRenames},
List<GitMergeFileFlag> fileFlags = const [GitMergeFileFlag.defaults], Set<GitMergeFileFlag> fileFlags = const {GitMergeFileFlag.defaults},
}) { }) {
var opts = <String, int>{}; var opts = <String, int>{};
opts['favor'] = favor.value; opts['favor'] = favor.value;
opts['mergeFlags'] = mergeFlags.fold( opts['mergeFlags'] =
0, mergeFlags.fold(0, (previousValue, e) => previousValue | e.value);
(previousValue, element) => previousValue + element.value, opts['fileFlags'] =
); fileFlags.fold(0, (previousValue, e) => previousValue | e.value);
opts['fileFlags'] = fileFlags.fold(
0,
(previousValue, element) => previousValue + element.value,
);
final result = merge_bindings.mergeCommits( final result = merge_bindings.mergeCommits(
_repoPointer, _repoPointer,
@ -638,17 +678,15 @@ class Repository {
/// HEAD will not be set to the reference [refName]. /// HEAD will not be set to the reference [refName].
void checkout({ void checkout({
String refName = '', String refName = '',
List<GitCheckout> strategy = const [ Set<GitCheckout> strategy = const {
GitCheckout.safe, GitCheckout.safe,
GitCheckout.recreateMissing GitCheckout.recreateMissing
], },
String? directory, String? directory,
List<String>? paths, List<String>? paths,
}) { }) {
final int strat = strategy.fold( final int strat =
0, strategy.fold(0, (previousValue, e) => previousValue | e.value);
(previousValue, element) => previousValue + element.value,
);
if (refName.isEmpty) { if (refName.isEmpty) {
checkout_bindings.index(_repoPointer, strat, directory, paths); checkout_bindings.index(_repoPointer, strat, directory, paths);

View file

@ -78,14 +78,14 @@ class RevSpec {
} }
/// The intent of the revspec. /// The intent of the revspec.
GitRevParse get flags { Set<GitRevParse> get flags {
final flag = _revSpecPointer.ref.flags; final flagInt = _revSpecPointer.ref.flags;
if (flag == 1) { var flags = <GitRevParse>{};
return GitRevParse.single; for (var flag in GitRevParse.values) {
} else if (flag == 2) { if (flagInt & flag.value == flag.value) {
return GitRevParse.range; flags.add(flag);
} else { }
return GitRevParse.mergeBase;
} }
return flags;
} }
} }

View file

@ -36,8 +36,11 @@ class RevWalk {
/// Changing the sorting mode resets the walker. /// Changing the sorting mode resets the walker.
/// ///
/// Throws a [LibGit2Error] if error occured. /// Throws a [LibGit2Error] if error occured.
void sorting(List<GitSort> sorting) { void sorting(Set<GitSort> sorting) {
final sort = sorting.fold(0, (previousValue, e) => e.value); final int sort = sorting.fold(
0,
(previousValue, e) => previousValue | e.value,
);
bindings.sorting(_revWalkPointer, sort); bindings.sorting(_revWalkPointer, sort);
} }

View file

@ -86,7 +86,8 @@ class TreeEntry {
/// Returns the UNIX file attributes of a tree entry. /// Returns the UNIX file attributes of a tree entry.
GitFilemode get filemode { GitFilemode get filemode {
return intToGitFilemode(bindings.entryFilemode(_treeEntryPointer)); final modeInt = bindings.entryFilemode(_treeEntryPointer);
return GitFilemode.values.singleWhere((mode) => modeInt == mode.value);
} }
@override @override

View file

@ -1,7 +1,6 @@
import 'dart:io'; import 'dart:io';
import 'dart:ffi'; import 'dart:ffi';
import 'bindings/libgit2_bindings.dart'; import 'bindings/libgit2_bindings.dart';
import 'git_types.dart';
DynamicLibrary loadLibrary() { DynamicLibrary loadLibrary() {
if (Platform.isLinux || Platform.isAndroid || Platform.isFuchsia) { if (Platform.isLinux || Platform.isAndroid || Platform.isFuchsia) {
@ -26,22 +25,3 @@ bool isValidShaHex(String str) {
return hexRegExp.hasMatch(str) && return hexRegExp.hasMatch(str) &&
(GIT_OID_MINPREFIXLEN <= str.length && GIT_OID_HEXSZ >= str.length); (GIT_OID_MINPREFIXLEN <= str.length && GIT_OID_HEXSZ >= str.length);
} }
GitFilemode intToGitFilemode(int i) {
switch (i) {
case 0:
return GitFilemode.unreadable;
case 16384:
return GitFilemode.tree;
case 33188:
return GitFilemode.blob;
case 33261:
return GitFilemode.blobExecutable;
case 40960:
return GitFilemode.link;
case 57344:
return GitFilemode.commit;
default:
return GitFilemode.unreadable;
}
}

View file

@ -29,7 +29,7 @@ void main() {
File('${tmpDir}feature_file').writeAsStringSync('edit'); File('${tmpDir}feature_file').writeAsStringSync('edit');
expect(repo.status, contains('feature_file')); expect(repo.status, contains('feature_file'));
repo.checkout(refName: 'HEAD', strategy: [GitCheckout.force]); repo.checkout(refName: 'HEAD', strategy: {GitCheckout.force});
expect(repo.status, isEmpty); expect(repo.status, isEmpty);
}); });
@ -37,7 +37,10 @@ void main() {
File('${repo.workdir}feature_file').writeAsStringSync('edit'); File('${repo.workdir}feature_file').writeAsStringSync('edit');
expect(repo.status, contains('feature_file')); expect(repo.status, contains('feature_file'));
repo.checkout(strategy: [GitCheckout.force]); repo.checkout(strategy: {
GitCheckout.force,
GitCheckout.conflictStyleMerge,
});
expect(repo.status, isEmpty); expect(repo.status, isEmpty);
}); });
@ -90,7 +93,12 @@ void main() {
refName: 'refs/heads/feature', refName: 'refs/heads/feature',
paths: ['another_feature_file'], paths: ['another_feature_file'],
); );
expect(repo.status, {'another_feature_file': GitStatus.indexNew.value}); expect(
repo.status,
{
'another_feature_file': {GitStatus.indexNew}
},
);
}); });
}); });
} }

View file

@ -31,7 +31,8 @@ void main() {
repo['c68ff54aabf660fcdd9a2838d401583fe31249e3'] as Commit; repo['c68ff54aabf660fcdd9a2838d401583fe31249e3'] as Commit;
final result = repo.mergeAnalysis(commit.id); final result = repo.mergeAnalysis(commit.id);
expect(result[0], GitMergeAnalysis.upToDate.value); expect(result[0], {GitMergeAnalysis.upToDate});
expect(result[1], {GitMergePreference.none});
expect(repo.status, isEmpty); expect(repo.status, isEmpty);
commit.free(); commit.free();
@ -42,7 +43,7 @@ void main() {
repo['c68ff54aabf660fcdd9a2838d401583fe31249e3'] as Commit; repo['c68ff54aabf660fcdd9a2838d401583fe31249e3'] as Commit;
final result = repo.mergeAnalysis(commit.id, 'refs/tags/v0.1'); final result = repo.mergeAnalysis(commit.id, 'refs/tags/v0.1');
expect(result[0], GitMergeAnalysis.upToDate.value); expect(result[0], {GitMergeAnalysis.upToDate});
expect(repo.status, isEmpty); expect(repo.status, isEmpty);
commit.free(); commit.free();
@ -61,7 +62,7 @@ void main() {
final result = repo.mergeAnalysis(theirHead.id, ffBranch.name); final result = repo.mergeAnalysis(theirHead.id, ffBranch.name);
expect( expect(
result[0], result[0],
GitMergeAnalysis.fastForward.value + GitMergeAnalysis.normal.value, {GitMergeAnalysis.fastForward, GitMergeAnalysis.normal},
); );
expect(repo.status, isEmpty); expect(repo.status, isEmpty);
@ -75,7 +76,7 @@ void main() {
repo['5aecfa0fb97eadaac050ccb99f03c3fb65460ad4'] as Commit; repo['5aecfa0fb97eadaac050ccb99f03c3fb65460ad4'] as Commit;
final result = repo.mergeAnalysis(commit.id); final result = repo.mergeAnalysis(commit.id);
expect(result[0], GitMergeAnalysis.normal.value); expect(result[0], {GitMergeAnalysis.normal});
expect(repo.status, isEmpty); expect(repo.status, isEmpty);
commit.free(); commit.free();
@ -87,13 +88,18 @@ void main() {
final index = repo.index; final index = repo.index;
final result = repo.mergeAnalysis(conflictBranch.target); final result = repo.mergeAnalysis(conflictBranch.target);
expect(result[0], GitMergeAnalysis.normal.value); expect(result[0], {GitMergeAnalysis.normal});
repo.merge(conflictBranch.target); repo.merge(conflictBranch.target);
expect(index.hasConflicts, true); expect(index.hasConflicts, true);
expect(index.conflicts.length, 1); expect(index.conflicts.length, 1);
expect(repo.state, GitRepositoryState.merge.value); expect(repo.state, GitRepositoryState.merge);
expect(repo.status, {'conflict_file': GitStatus.conflicted.value}); expect(
repo.status,
{
'conflict_file': {GitStatus.conflicted}
},
);
final conflictedFile = index.conflicts['conflict_file']!; final conflictedFile = index.conflicts['conflict_file']!;
expect(conflictedFile.ancestor, null); expect(conflictedFile.ancestor, null);
@ -104,7 +110,12 @@ void main() {
index.write(); index.write();
expect(index.hasConflicts, false); expect(index.hasConflicts, false);
expect(index.conflicts, isEmpty); expect(index.conflicts, isEmpty);
expect(repo.status, {'conflict_file': GitStatus.indexModified.value}); expect(
repo.status,
{
'conflict_file': {GitStatus.indexModified}
},
);
index.free(); index.free();
conflictBranch.free(); conflictBranch.free();
@ -172,6 +183,32 @@ void main() {
ourCommit.free(); ourCommit.free();
theirCommit.free(); theirCommit.free();
}); });
test('successfully merges with provided merge and file flags', () {
final theirCommit =
repo['5aecfa0fb97eadaac050ccb99f03c3fb65460ad4'] as Commit;
final ourCommit =
repo['14905459d775f3f56a39ebc2ff081163f7da3529'] as Commit;
final mergeIndex = repo.mergeCommits(
ourCommit: ourCommit,
theirCommit: theirCommit,
mergeFlags: {
GitMergeFlag.findRenames,
GitMergeFlag.noRecursive,
},
fileFlags: {
GitMergeFileFlag.ignoreWhitespace,
GitMergeFileFlag.ignoreWhitespaceEOL,
GitMergeFileFlag.styleMerge,
},
);
expect(mergeIndex.conflicts, isEmpty);
mergeIndex.free();
ourCommit.free();
theirCommit.free();
});
}); });
group('merge trees', () { group('merge trees', () {
@ -246,7 +283,7 @@ void main() {
test('successfully cherry-picks commit', () { test('successfully cherry-picks commit', () {
final cherry = repo['5aecfa0fb97eadaac050ccb99f03c3fb65460ad4'] as Commit; final cherry = repo['5aecfa0fb97eadaac050ccb99f03c3fb65460ad4'] as Commit;
repo.cherryPick(cherry); repo.cherryPick(cherry);
expect(repo.state, GitRepositoryState.cherrypick.value); expect(repo.state, GitRepositoryState.cherrypick);
expect(repo.message, 'add another feature file\n'); expect(repo.message, 'add another feature file\n');
final index = repo.index; final index = repo.index;
expect(index.conflicts, isEmpty); expect(index.conflicts, isEmpty);

View file

@ -343,7 +343,13 @@ void main() {
final index = repo.index; final index = repo.index;
index.remove('file'); index.remove('file');
index.add('new_file.txt'); index.add('new_file.txt');
expect(repo.status, {'file': 132, 'new_file.txt': 1}); expect(
repo.status,
{
'file': {GitStatus.indexDeleted, GitStatus.wtNew},
'new_file.txt': {GitStatus.indexNew}
},
);
index.free(); index.free();
}); });
@ -351,7 +357,11 @@ void main() {
test('returns status of a single file for provided path', () { test('returns status of a single file for provided path', () {
final index = repo.index; final index = repo.index;
index.remove('file'); index.remove('file');
expect(repo.statusFile('file'), 132); expect(
repo.statusFile('file'),
{GitStatus.indexDeleted, GitStatus.wtNew},
);
expect(repo.statusFile('.gitignore'), {GitStatus.current});
index.free(); index.free();
}); });

View file

@ -81,7 +81,7 @@ void main() {
expect(revspec.from.id.sha, headSHA); expect(revspec.from.id.sha, headSHA);
expect(revspec.to, isNull); expect(revspec.to, isNull);
expect(revspec.flags, GitRevParse.single); expect(revspec.flags, {GitRevParse.single});
revspec.from.free(); revspec.from.free();
@ -89,7 +89,7 @@ void main() {
expect(revspec.from.id.sha, parentSHA); expect(revspec.from.id.sha, parentSHA);
expect(revspec.to?.id.sha, '5aecfa0fb97eadaac050ccb99f03c3fb65460ad4'); expect(revspec.to?.id.sha, '5aecfa0fb97eadaac050ccb99f03c3fb65460ad4');
expect(revspec.flags, GitRevParse.range); expect(revspec.flags, {GitRevParse.range});
revspec.from.free(); revspec.from.free();
revspec.to?.free(); revspec.to?.free();
@ -98,7 +98,7 @@ void main() {
expect(revspec.from.id.sha, headSHA); expect(revspec.from.id.sha, headSHA);
expect(revspec.to?.id.sha, '5aecfa0fb97eadaac050ccb99f03c3fb65460ad4'); expect(revspec.to?.id.sha, '5aecfa0fb97eadaac050ccb99f03c3fb65460ad4');
expect(revspec.flags, GitRevParse.mergeBase); expect(revspec.flags, {GitRevParse.range, GitRevParse.mergeBase});
expect( expect(
repo.mergeBase(revspec.from.id.sha, revspec.to!.id.sha), repo.mergeBase(revspec.from.id.sha, revspec.to!.id.sha),
isA<Oid>(), isA<Oid>(),

View file

@ -53,7 +53,7 @@ void main() {
final start = Oid.fromSHA(repo, log.first); final start = Oid.fromSHA(repo, log.first);
walker.push(start); walker.push(start);
walker.sorting([GitSort.reverse]); walker.sorting({GitSort.reverse});
final commits = walker.walk(); final commits = walker.walk();
for (var i = 0; i < commits.length; i++) { for (var i = 0; i < commits.length; i++) {
@ -77,7 +77,7 @@ void main() {
expect(timeSortedCommits[i].id.sha, log[i]); expect(timeSortedCommits[i].id.sha, log[i]);
} }
walker.sorting([GitSort.time, GitSort.reverse]); walker.sorting({GitSort.time, GitSort.reverse});
final reverseSortedCommits = walker.walk(); final reverseSortedCommits = walker.walk();
for (var i = 0; i < reverseSortedCommits.length; i++) { for (var i = 0; i < reverseSortedCommits.length; i++) {
expect(reverseSortedCommits[i].id.sha, log.reversed.toList()[i]); expect(reverseSortedCommits[i].id.sha, log.reversed.toList()[i]);