Coder Social home page Coder Social logo

gift's Introduction

Gift (Deprecated)

Build Status

This library isn't really maintained anymore. You should use SwiftGit2 instead.

Gift provides Swift bindings to the libgit2 library.

That means you can clone a Git repository, list its commits, or even make a commit of your own--all from within your Swift program.

For example, we can grab the latest commit message of this repository:

let url = NSURL(fileURLWithPath: "/Users/modocache/Gift")!
let latestCommitMessage = openRepository(url)
  .flatMap { $0.headReference }
  .flatMap { $0.commit }
  .flatMap { $0.message }

Gift returns result objects for any operation that might fail. The type of lastCommitMessage above is Result<String, NSError>. If every operation succeeded, you can access the commit message String. But if any of the operations failed, you'll have an NSError that describes what went wrong.

Features

You can list all branches in a repository. Gift uses ReactiveCocoa to represent a sequence of values:

repository.branches().start(next: { (reference) in
  println("Branch name: \(reference.name)")
})

You can list commits as well:

repository.commits().start { (commit) in
  println("Commit message: \(commit.message)")
}

You can order the commits as you please, of course:

let commits = repository.commits(sorting: CommitSorting.Time | CommitSorting.Reverse)

You can make a commit, too:

let tree = repository.index        // Grab the index for the repository
  .flatMap { $0.add() }            // Add any modified entries to that index
  .flatMap { $0.writeTree() }      // Grab a tree representing the changeset
  .flatMap { $0.commit("Zing!") }  // Commit

Swift allows Gift to provide default parameters. If you want to use the default behavior, you can easily clone a remote repository:

let remoteURL = NSURL(string: "git://git.libssh2.org/libssh2.git")!
let destinationURL = NSURL(fileURLWithPath: "/Users/modocache/libssh2")!
let repository = cloneRepository(remoteURL, destinationURL)

But you can also customize that behavior and have Gift issue download progress updates:

let options = CloneOptions(
  checkoutOptions: CheckoutOptions(
    strategy: CheckoutStrategy.SafeCreate,
    progressCallback: { (path, completedSteps, totalSteps) in
      // ...do something with checkout progress.
    }
  ),
  remoteCallbacks: RemoteCallbacks(
    transportMessageCallback: { (message) in
      // ...do something with messages from remote, like "Compressing objects: 1% (47/4619)"
    },
    transferProgressCallback: { (progress) in
      // ...do something with progress (bytes received, etc.) updates.
    })
)
let repository = cloneRepository(remoteURL, destinationURL, options: options)

Why Not ObjectiveGit?

ObjectiveGit is the official set of Objective-C bindings to Git. It is maintained by the same people who develop GitHub for Mac, so you can be sure it's solid. And you can use it from Swift!

If, however, you're willing to tolerate a less stable set of bindings, Gift utilizes features of Swift to make certain aspects of interfacing with libgit2 easy. For example, take the following code, which prints the latest commit message in a repository:

let latestCommitMessage = openRepository(url)
  .flatMap { $0.headReference }
  .flatMap { $0.commit }
  .flatMap { $0.message }
println(latestCommitMessage) // Either prints commit or error

Because most Gift functions return Result objects, and because you can chain those Result objects, Gift makes it easy to display precise error information. To get an equivalent level of error handling in ObjectiveGit, you'd need to write the following:

NSError *repositoryError = nil;
GTRepository *repository = [GTRepository repositoryWithURL:url error:&error];
if (repositoryError != nil) {
  NSLog(@"An error occurred: %@", repositoryError);
  return;
}

NSError *headReferenceError = nil;
GTReference *headReference = [repository headReferenceWithError:&headReferenceError];
if (headReferenceError != nil) {
  NSLog(@"An error occurred: %@", headReferenceError);
  return;
}

NSError *lookupError = nil;
GTCommit *commit = [repository lookupObjectByOID:headReference.OID
                                      objectType:GTObjectTypeCommit
                                           error:&lookupError];
if (lookupError != nil) {
  NSLog(@"An error occurred: %@", lookupError);
  return;
}

NSString *message = commit.message;
if (message == nil) {
  NSLog(@"An error occurred");
} else {
  NSLog(@"Commit message: %@", message);
}

As you can see, Gift requires significantly less code. ObjectiveGit is, however, far more mature. It's up to you to decide which is right for your project.

How to Build

You'll need to have homebrew installed. If you don't already have CMake installed, run:

$ brew install cmake

Then, to build the dependencies for Gift, just run:

$ rake dependencies build

If it's your first time running the script, it'll take a while--it needs to build static libraries for OpenSSL, libssh2, and libgit2. And that's for both OS X and iOS.

Testing Your Changes

You can test your changes by running:

$ rake

Build Errors

If you see a non-descript error like "Cannot build module Gift", there's an extremely sophisticated workaround: first, remove all the source files from the main and test target you're trying to build.

Then, build the framework. It should work fine (magic, I know). Now that it builds, revert the removal of the files by running git checkout -- Gift.xcodeproj. And voila! Now everything builds fine.

How to Use Gift in Your iOS App

You can find an example iOS app that uses Gift in the Examples directory. Using Gift is (sort of) easy:

  1. Drag Gift.xcodeproj, LlamaKit.xcodeproj, Quick.xcodeproj, and Nimble.xcodeproj into your app's workspace.
  2. In the "Link Binary with Libraries" build phase of your app, link your app to Gift.framework.
  3. Set the “Header Search Paths” (HEADER_SEARCH_PATHS) build setting to the correct path for the libgit2 headers in your project. For example, if you added the submodule to your project as External/Gift, you would set this build setting to External/Gift/External/libgit2/include.
  4. import Gift in any Swift file, and you're off to the races!

gift's People

Contributors

akatz avatar dbgrandi avatar kastiglione avatar modocache avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

gift's Issues

StatusDeltaOptions should create a git_strarray for the given paths

StatusDeltaOptions takes an array of paths when initialized. This is a list of path or path patterns to files that should be included in the status delta enumeration. However, these paths are not passed to libgit2--thus any paths specified by the user are ignored.

Were Gift to allocate unsafe pointers to those paths, to be used by git_strarray, those pointers would have to be freed after they are no longer needed. Gift doesn't have a good way to ensure those pointers are freed yet.

Expand CheckoutOptions

Gift allows users to specify some aspects of how a revision is checked out. But libgit2 provides even more options that Gift has yet to support. CheckoutOptions should also allow users to configure the following libgit2 git_checkout_options attributes:

  • int disable_filters, a boolean indicating whether to disable some filters, like CRLF conversion
  • unsigned int dir_mode
  • unsigned int file_mode
  • int file_open_flags
  • unsigned int notify_flags, a set of git_checkout_notify_t flags
  • git_checkout_notify_cb notify_cb, a callback fired when an event matching the flags above occurs
  • git_strarray paths, a set of paths to checkout
  • git_tree *baseline
  • const char *target_directory, an alternative checkout path for the working directory
  • const char *ancestor_label
  • const char *our_label
  • const char *their_label
  • git_checkout_perfdata_cb perfdata_cb

Add continuous integration script

Contributors should be able to ensure all tests pass before submitting a pull request. Once the repository is made public, Travis CI will use this script to test the repository.

For an example, see Quick's test script.

As I mentioned in #4, I think this should be in a Rakefile or something equally easy to maintain.

More features!

Gift already includes all of the infrastructure needed to wrap libgit2 in clean Swift functions, as well as all the test infrastructure needed to test those wrappers. Huzzah!

But it could use some more features. Using Gift, I should be able to (in descending order of importance/difficulty):

  • Tag a particular commit
  • List all branches in a repository
  • Commit changes in a repository
  • Merge changes into a branch from another branch
  • Retrieve the name of the author of the last commit in a repository
  • Checkout a particular reference in a repository
  • Add and remove a remote repository, i.e.: git remote add and git remote rm
  • Fetch from a remote repository
  • Reset changes in a repository, i.e.: git reset
  • Run git blame on a particular file in a repository
  • Run git stash save and git stash apply on a working directory

The following features might be worth working on later:

  • List all tags in a repository. git_tag_foreach takes a callback function pointer to a C function--Swift doesn't make it easy to interop with such an API, so this will require writing a C function, getting a pointer to it from Swift, etc. (EDIT: It's possible to use progress callback APIs from Swift, see: dbaeace.)

Note that a "remote" repository doesn't have to be an external resource. We can fetch from remotes that exist on the local filesystem--and we should, in order to keep our tests fast!

We don't have to include all these features in the v0.1.0 release, but the more the better!

Linker flags to brew installed libraries

I just installed libssl, and my version is different from the version specified in the project file.

Here's some output from the changes I made to Gift.xcodeproj/project.pbxproj

-                                       /usr/local/Cellar/openssl/1.0.1l/lib/libssl.a,
-                                       /usr/local/Cellar/openssl/1.0.1l/lib/libcrypto.a,
+                                       /usr/local/Cellar/openssl/1.0.1j_1/lib/libssl.a,
+                                       /usr/local/Cellar/openssl/1.0.1j_1/lib/libcrypto.a,

Add bootstrapping script

New contributors should be able to setup the repository's dependencies in a single line on the command line--they're currently just explained (poorly) on the README.

We should consider using a Rakefile--I can stomach the script being dependent on Ruby being installed if that makes it clearer and easier to maintain.

Tests for the Rake tasks would be awesome. But is that even possible?

Support stashing

I want to be able to stash uncommitted changes:

let repository = openRepository(NSURL(fileURLWithPath: "/Users/modocache/Desktop/MyRepo")!)
let index = repository.flatMap { $0.index }
let commit = index.flatMap { $0.stash(message: "Saving some work for another day") }

I'd also like to be able to apply stashed changes somehow. Not sure what that API should look like yet, but suggestions welcome!

Swift 2 Support

I started playing around with updating Gift for Swift 2, and was wondering if there has been any work towards this elsewhere? I don't see any branches for it.

One question that I have is what the best course of action would be with regards to error handling. It appears that LlamaKit has been subsumed into the Result project, but Swift 2 also has native error handling capabilities now. Is the Result approach with an Either type preferred, or Swift's own native error handling going forward?

I'd be willing to put some work into this, but don't want to head down a path that isn't the preferred approach.

Eradicate TODOs

Most of the TODOs in the project right now are for missing documentation. Remove all TODOs, either by resolving them or by creating a GitHub issue to track it.

Expand RepositoryInitializationOptions

Gift allows users to specify how a repository should be initialized. But libgit2 provides even more callbacks that Gift has yet to support. RepositoryInitializationOptions should also allow users to configure the following libgit2 git_repository_init_options attributes:

  • const char *workdir_path
  • const char *description
  • const char *template_path
  • const char *initial_head
  • const char *origin_url

Support remotes

libgit2 defined a git_remote struct, which is something Gift has no notion of. Port git_remote to Gift in order to support remote operations, like fetching from and pushing to a remote repository.

Expand RemoteCallbacks

Gift allows users to specify callbacks that should be executed when performing remote operations, such as a clone or push. But libgit2 provides even more callbacks that Gift has yet to support. RemoteCallbacks should also allow users to specify the following libgit2 git_remote_callbacks callbacks:

  • git_cred_acquire_cb credentials, used when the remote requires authentication.
  • git_transport_certificate_check_cb certificate_check, used to determine whether an unauthenticated connection should proceed
  • int (*update_tips)(const char *refname, const git_oid *a, const git_oid *b, void *data), fired whenever a local reference is updated by the remote operation
  • git_packbuilder_progress pack_progress, provides progress information on packing
  • git_push_transfer_progress push_transfer_progress, provides progress information on pushing
  • int (*push_update_reference)(const char *refname, const char *status, void *data), called when references are updated due to a push

``rake dependencies build`` fails

rake dependencies build is broken on my Mac. I haven't tried to debug it yet.

Just a git clone and then rake dependencies build.

[ 96%] Building C object CMakeFiles/git2.dir/src/unix/map.c.o
[ 97%] Building C object CMakeFiles/git2.dir/src/unix/realpath.c.o
[ 98%] Building C object CMakeFiles/git2.dir/deps/http-parser/http_parser.c.o
[ 99%] Building C object CMakeFiles/git2.dir/src/hash/hash_generic.c.o
In file included from /Users/erg/sedit/deps/Gift/External/libgit2/src/hash/hash_generic.c:10:
/Users/erg/sedit/deps/Gift/External/libgit2/src/hash/hash_generic.h:13:8: error: redefinition of 'git_hash_ctx'
struct git_hash_ctx {
       ^
/Users/erg/sedit/deps/Gift/External/libgit2/src/hash/hash_common_crypto.h:15:8: note: previous definition is here
struct git_hash_ctx {
       ^
/Users/erg/sedit/deps/Gift/External/libgit2/src/hash/hash_generic.c:121:11: error: no member named 'H' in 'struct git_hash_ctx'
        A = ctx->H[0];
            ~~~  ^
/Users/erg/sedit/deps/Gift/External/libgit2/src/hash/hash_generic.c:122:11: error: no member named 'H' in 'struct git_hash_ctx'
        B = ctx->H[1];
            ~~~  ^
/Users/erg/sedit/deps/Gift/External/libgit2/src/hash/hash_generic.c:123:11: error: no member named 'H' in 'struct git_hash_ctx'
        C = ctx->H[2];
            ~~~  ^
/Users/erg/sedit/deps/Gift/External/libgit2/src/hash/hash_generic.c:124:11: error: no member named 'H' in 'struct git_hash_ctx'
        D = ctx->H[3];
            ~~~  ^
/Users/erg/sedit/deps/Gift/External/libgit2/src/hash/hash_generic.c:125:11: error: no member named 'H' in 'struct git_hash_ctx'
        E = ctx->H[4];
            ~~~  ^
/Users/erg/sedit/deps/Gift/External/libgit2/src/hash/hash_generic.c:217:7: error: no member named 'H' in 'struct git_hash_ctx'
        ctx->H[0] += A;
        ~~~  ^
/Users/erg/sedit/deps/Gift/External/libgit2/src/hash/hash_generic.c:218:7: error: no member named 'H' in 'struct git_hash_ctx'
        ctx->H[1] += B;
        ~~~  ^
/Users/erg/sedit/deps/Gift/External/libgit2/src/hash/hash_generic.c:219:7: error: no member named 'H' in 'struct git_hash_ctx'
        ctx->H[2] += C;
        ~~~  ^
/Users/erg/sedit/deps/Gift/External/libgit2/src/hash/hash_generic.c:220:7: error: no member named 'H' in 'struct git_hash_ctx'
        ctx->H[3] += D;
        ~~~  ^
/Users/erg/sedit/deps/Gift/External/libgit2/src/hash/hash_generic.c:221:7: error: no member named 'H' in 'struct git_hash_ctx'
        ctx->H[4] += E;
        ~~~  ^
/Users/erg/sedit/deps/Gift/External/libgit2/src/hash/hash_generic.c:224:5: error: redefinition of 'git_hash_init'
int git_hash_init(git_hash_ctx *ctx)
    ^
/Users/erg/sedit/deps/Gift/External/libgit2/src/hash/hash_common_crypto.h:23:17: note: previous definition is here
GIT_INLINE(int) git_hash_init(git_hash_ctx *ctx)
                ^
/Users/erg/sedit/deps/Gift/External/libgit2/src/hash/hash_generic.c:226:7: error: no member named 'size' in 'struct git_hash_ctx'
        ctx->size = 0;
        ~~~  ^
/Users/erg/sedit/deps/Gift/External/libgit2/src/hash/hash_generic.c:229:7: error: no member named 'H' in 'struct git_hash_ctx'
        ctx->H[0] = 0x67452301;
        ~~~  ^
/Users/erg/sedit/deps/Gift/External/libgit2/src/hash/hash_generic.c:230:7: error: no member named 'H' in 'struct git_hash_ctx'
        ctx->H[1] = 0xefcdab89;
        ~~~  ^
/Users/erg/sedit/deps/Gift/External/libgit2/src/hash/hash_generic.c:231:7: error: no member named 'H' in 'struct git_hash_ctx'
        ctx->H[2] = 0x98badcfe;
        ~~~  ^
/Users/erg/sedit/deps/Gift/External/libgit2/src/hash/hash_generic.c:232:7: error: no member named 'H' in 'struct git_hash_ctx'
        ctx->H[3] = 0x10325476;
        ~~~  ^
/Users/erg/sedit/deps/Gift/External/libgit2/src/hash/hash_generic.c:233:7: error: no member named 'H' in 'struct git_hash_ctx'
        ctx->H[4] = 0xc3d2e1f0;
        ~~~  ^
/Users/erg/sedit/deps/Gift/External/libgit2/src/hash/hash_generic.c:238:5: error: redefinition of 'git_hash_update'
int git_hash_update(git_hash_ctx *ctx, const void *data, size_t len)
    ^
/Users/erg/sedit/deps/Gift/External/libgit2/src/hash/hash_common_crypto.h:30:17: note: previous definition is here
GIT_INLINE(int) git_hash_update(git_hash_ctx *ctx, const void *data, size_t len)
/*
                ^
fatal error: too many errors emitted, stopping now [-ferror-limit=]
20 errors generated.
make[2]: *** [CMakeFiles/git2.dir/src/hash/hash_generic.c.o] Error 1
make[1]: *** [CMakeFiles/git2.dir/all] Error 2
make: *** [all] Error 2
rake aborted!
RAKE TASK FAILED:         mkdir -p External/libgit2/build && \
        cd External/libgit2/build && \
        cmake -DBUILD_SHARED_LIBS:BOOL=OFF -DBUILD_CLAR:BOOL=OFF -DTHREADSAFE:BOOL=ON ..
        cmake --build .
/Users/erg/sedit/deps/Gift/Scripts/helpers.rb:3:in `run'
Scripts/build_osx.rake:22:in `block (3 levels) in <top (required)>'
Tasks: TOP => build => build:osx => build:osx:libgit2
(See full trace by running task with --trace)

Support git_blame_file

I want to be able to get the blame information for a file in a repository:

let repository = cloneRepository(NSURL(string: "git://git.libssh2.org/libssh2.git")!)
let blame: Result<Blame, NSError> = repository.flatMap { $0.blame("README.md") }

I want to be able to see who was responsible for which lines. That probably involves porting git_blame_hunk to Gift as well. Not sure what the API for that should look like, but suggestions welcome!

Expand CloneOptions

Gift allows users to specify some aspects of how a repository is cloned. But libgit2 provides even more options that Gift has yet to support. CloneOptions should also allow users to configure the following libgit2 git_clone_options attributes:

  • int bare
  • const char* checkout_branch, the name of the branch to checkout after cloning
  • git_signature *signature, the identity used when updating the reflog
  • git_repository_create_cb repository_cb
  • git_remote_create_cb remote_cb, a callback used to create the git_remote. Dependent upon #18.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.