import 'dart:ffi'; import 'package:ffi/ffi.dart'; import '../error.dart'; import 'libgit2_bindings.dart'; import '../util.dart'; /// Get the type of a reference. int referenceType(Pointer ref) => libgit2.git_reference_type(ref); /// Get the OID pointed to by a direct reference. /// /// Only available if the reference is direct (i.e. an object id reference, not a symbolic one). /// /// Throws an exception if error occured. Pointer target(Pointer ref) { final result = libgit2.git_reference_target(ref); if (result == nullptr) { throw Exception('OID for reference isn\'t available'); } else { return result; } } /// Resolve a symbolic reference to a direct reference. /// /// This method iteratively peels a symbolic reference until it resolves /// to a direct reference to an OID. /// /// The peeled reference must be freed manually once it's no longer needed. /// /// If a direct reference is passed as an argument, a copy of that reference is returned. /// This copy must be manually freed too. /// /// Throws a [LibGit2Error] if error occured. Pointer resolve(Pointer ref) { final out = calloc>(); final error = libgit2.git_reference_resolve(out, ref); if (error < 0) { throw LibGit2Error(libgit2.git_error_last()); } else { return out.value; } } /// Lookup a reference by name in a repository. /// /// The returned reference must be freed by the user. /// /// The name will be checked for validity. /// /// Throws a [LibGit2Error] if error occured. Pointer lookup(Pointer repo, String name) { final out = calloc>(); final nameC = name.toNativeUtf8().cast(); final error = libgit2.git_reference_lookup(out, repo, nameC); calloc.free(nameC); if (error < 0) { throw LibGit2Error(libgit2.git_error_last()); } else { return out.value; } } /// Lookup a reference by DWIMing its short name. /// /// Apply the git precendence rules to the given shorthand to determine which reference /// the user is referring to. /// /// Throws a [LibGit2Error] if error occured. Pointer lookupDWIM(Pointer repo, String name) { final out = calloc>(); final nameC = name.toNativeUtf8().cast(); final error = libgit2.git_reference_dwim(out, repo, nameC); calloc.free(nameC); if (error < 0) { throw LibGit2Error(libgit2.git_error_last()); } else { return out.value; } } /// Get the full name of a reference. String name(Pointer ref) { var result = calloc(); result = libgit2.git_reference_name(ref); return result.cast().toDartString(); } /// Get the reference's short name. /// /// This will transform the reference name into a name "human-readable" version. /// If no shortname is appropriate, it will return the full name. String shorthand(Pointer ref) { final result = libgit2.git_reference_shorthand(ref); return result.cast().toDartString(); } /// Rename an existing reference. /// /// This method works for both direct and symbolic references. /// /// The new name will be checked for validity. /// /// If the force flag is not enabled, and there's already a reference with the given name, /// the renaming will fail. /// /// IMPORTANT: The user needs to write a proper reflog entry if the reflog is enabled for /// the repository. We only rename the reflog if it exists. /// /// Throws a [LibGit2Error] if error occured. Pointer rename( Pointer ref, String newName, bool force, String? logMessage, ) { final out = calloc>(); final newNameC = newName.toNativeUtf8().cast(); final forceC = force == true ? 1 : 0; final logMessageC = logMessage?.toNativeUtf8().cast() ?? nullptr; final error = libgit2.git_reference_rename( out, ref, newNameC, forceC, logMessageC, ); calloc.free(newNameC); calloc.free(logMessageC); if (error < 0) { throw LibGit2Error(libgit2.git_error_last()); } else { return out.value; } } /// Fill a list with all the references that can be found in a repository. /// /// The string array will be filled with the names of all references; /// these values are owned by the user and should be free'd manually when no longer needed. /// /// Throws a [LibGit2Error] if error occured. List list(Pointer repo) { var array = calloc(); final error = libgit2.git_reference_list(array, repo); var result = []; if (error < 0) { throw LibGit2Error(libgit2.git_error_last()); } else { for (var i = 0; i < array.ref.count; i++) { result.add( array.ref.strings.elementAt(i).value.cast().toDartString()); } } calloc.free(array); return result; } /// Check if a reflog exists for the specified reference. /// /// Throws a [LibGit2Error] if error occured. bool hasLog(Pointer repo, String name) { final refname = name.toNativeUtf8().cast(); final error = libgit2.git_reference_has_log(repo, refname); calloc.free(refname); if (error < 0) { throw LibGit2Error(libgit2.git_error_last()); } else { return error == 1 ? true : false; } } /// Check if a reference is a local branch. bool isBranch(Pointer ref) { final result = libgit2.git_reference_is_branch(ref); return result == 1 ? true : false; } /// Check if a reference is a note. bool isNote(Pointer ref) { final result = libgit2.git_reference_is_note(ref); return result == 1 ? true : false; } /// Check if a reference is a remote tracking branch. bool isRemote(Pointer ref) { final result = libgit2.git_reference_is_remote(ref); return result == 1 ? true : false; } /// Check if a reference is a tag. bool isTag(Pointer ref) { final result = libgit2.git_reference_is_tag(ref); return result == 1 ? true : false; } /// Create a new direct reference. /// /// A direct reference (also called an object id reference) refers directly to a /// specific object id (a.k.a. OID or SHA) in the repository. The id permanently refers to /// the object (although the reference itself can be moved). For example, in libgit2 /// the direct ref "refs/tags/v0.17.0" refers to OID 5b9fac39d8a76b9139667c26a63e6b3f204b3977. /// /// The direct reference will be created in the repository and written to the disk. /// The generated reference object must be freed by the user. /// /// Valid reference names must follow one of two patterns: /// /// Top-level names must contain only capital letters and underscores, and must begin and end /// with a letter. (e.g. "HEAD", "ORIG_HEAD"). /// Names prefixed with "refs/" can be almost anything. You must avoid the characters /// '~', '^', ':', '\', '?', '[', and '*', and the sequences ".." and "@{" which have /// special meaning to revparse. /// This function will throw a [LibGit2Error] if a reference already exists with the given name /// unless force is true, in which case it will be overwritten. /// /// The message for the reflog will be ignored if the reference does not belong in the /// standard set (HEAD, branches and remote-tracking branches) and it does not have a reflog. Pointer createDirect( Pointer repo, String name, Pointer oid, bool force, String? logMessage, ) { final out = calloc>(); final nameC = name.toNativeUtf8().cast(); final forceC = force == true ? 1 : 0; final logMessageC = logMessage?.toNativeUtf8().cast() ?? nullptr; final error = libgit2.git_reference_create( out, repo, nameC, oid, forceC, logMessageC, ); calloc.free(nameC); calloc.free(logMessageC); if (error < 0) { throw (LibGit2Error(libgit2.git_error_last())); } else { return out.value; } } /// Create a new symbolic reference. /// /// A symbolic reference is a reference name that refers to another reference name. /// If the other name moves, the symbolic name will move, too. As a simple example, /// the "HEAD" reference might refer to "refs/heads/master" while on the "master" branch /// of a repository. /// /// The symbolic reference will be created in the repository and written to the disk. /// The generated reference object must be freed by the user. /// /// Valid reference names must follow one of two patterns: /// /// Top-level names must contain only capital letters and underscores, and must begin and end /// with a letter. (e.g. "HEAD", "ORIG_HEAD"). /// Names prefixed with "refs/" can be almost anything. You must avoid the characters /// '~', '^', ':', '\', '?', '[', and '*', and the sequences ".." and "@{" which have special /// meaning to revparse. /// This function will throw an [LibGit2Error] if a reference already exists with the given /// name unless force is true, in which case it will be overwritten. /// /// The message for the reflog will be ignored if the reference does not belong in the standard /// set (HEAD, branches and remote-tracking branches) and it does not have a reflog. Pointer createSymbolic( Pointer repo, String name, String target, bool force, String? logMessage, ) { final out = calloc>(); final nameC = name.toNativeUtf8().cast(); final targetC = target.toNativeUtf8().cast(); final forceC = force == true ? 1 : 0; final logMessageC = logMessage?.toNativeUtf8().cast() ?? nullptr; final error = libgit2.git_reference_symbolic_create( out, repo, nameC, targetC, forceC, logMessageC, ); calloc.free(nameC); calloc.free(targetC); calloc.free(logMessageC); if (error < 0) { throw (LibGit2Error(libgit2.git_error_last())); } else { return out.value; } } /// Delete an existing reference. /// /// This method works for both direct and symbolic references. /// The reference will be immediately removed on disk but the memory will not be freed. /// /// Throws a [LibGit2Error] if the reference has changed from the time it was looked up. void delete(Pointer ref) { final error = libgit2.git_reference_delete(ref); if (error < 0) { throw LibGit2Error(libgit2.git_error_last()); } } /// Get the repository where a reference resides. Pointer owner(Pointer ref) { return libgit2.git_reference_owner(ref); } /// Conditionally create a new reference with the same name as the given reference /// but a different OID target. The reference must be a direct reference, otherwise this will fail. /// /// The new reference will be written to disk, overwriting the given reference. /// /// Throws a [LibGit2Error] if error occured. Pointer setTarget( Pointer ref, Pointer oid, String? logMessage, ) { final out = calloc>(); final logMessageC = logMessage?.toNativeUtf8().cast() ?? nullptr; final error = libgit2.git_reference_set_target(out, ref, oid, logMessageC); calloc.free(logMessageC); if (error < 0) { throw LibGit2Error(libgit2.git_error_last()); } else { return out.value; } } /// Create a new reference with the same name as the given reference but a different /// symbolic target. The reference must be a symbolic reference, otherwise this will fail. /// /// The new reference will be written to disk, overwriting the given reference. /// /// The target name will be checked for validity. /// /// The message for the reflog will be ignored if the reference does not belong in the /// standard set (HEAD, branches and remote-tracking branches) and and it does not have a reflog. /// /// Throws a [LibGit2Error] if error occured. Pointer setTargetSymbolic( Pointer ref, String target, String? logMessage, ) { final out = calloc>(); final targetC = target.toNativeUtf8().cast(); final logMessageC = logMessage?.toNativeUtf8().cast() ?? nullptr; final error = libgit2.git_reference_symbolic_set_target(out, ref, targetC, logMessageC); calloc.free(targetC); calloc.free(logMessageC); if (error < 0) { throw LibGit2Error(libgit2.git_error_last()); } else { return out.value; } } /// Ensure the reference name is well-formed. /// /// Valid reference names must follow one of two patterns: /// /// Top-level names must contain only capital letters and underscores, /// and must begin and end with a letter. (e.g. "HEAD", "ORIG_HEAD"). /// Names prefixed with "refs/" can be almost anything. You must avoid /// the characters '~', '^', ':', '\', '?', '[', and '*', and the sequences ".." /// and "@{" which have special meaning to revparse. bool isValidName(String name) { final refname = name.toNativeUtf8().cast(); final result = libgit2.git_reference_is_valid_name(refname); calloc.free(refname); return result == 1 ? true : false; } /// Free the given reference. void free(Pointer ref) => libgit2.git_reference_free(ref);