summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--examples/network/Makefile23
-rw-r--r--examples/network/common.h14
-rw-r--r--examples/network/fetch.c127
-rw-r--r--examples/network/git2.c57
-rw-r--r--examples/network/index-pack.c47
-rw-r--r--examples/network/ls-remote.c104
6 files changed, 372 insertions, 0 deletions
diff --git a/examples/network/Makefile b/examples/network/Makefile
new file mode 100644
index 000000000..59a607632
--- /dev/null
+++ b/examples/network/Makefile
@@ -0,0 +1,23 @@
+default: all
+
+# If you've installed libgit2 to a non-standard location, you can use
+# these lines to make pkg-config find it.
+
+#LIBGIT2_PATH ?= $(HOME)/staging/libgit2/lib DEPS =
+#$(shell PKG_CONFIG_PATH=$(LIBGIT2_PATH)/pkgconfig pkg-config --cflags
+#--libs libgit2)
+
+DEPS = $(shell pkg-config --cflags --libs libgit2)
+
+CC = gcc
+CFLAGS += -g
+CFLAGS += $(DEPS)
+
+OBJECTS = \
+ git2.o \
+ ls-remote.o \
+ fetch.o \
+ index-pack.o
+
+all: $(OBJECTS)
+ $(CC) $(CFLAGS) -o git2 $(OBJECTS)
diff --git a/examples/network/common.h b/examples/network/common.h
new file mode 100644
index 000000000..29460bb36
--- /dev/null
+++ b/examples/network/common.h
@@ -0,0 +1,14 @@
+#ifndef __COMMON_H__
+#define __COMMON_H__
+
+#include <git2.h>
+
+typedef int (*git_cb)(git_repository *, int , char **);
+
+int ls_remote(git_repository *repo, int argc, char **argv);
+int parse_pkt_line(git_repository *repo, int argc, char **argv);
+int show_remote(git_repository *repo, int argc, char **argv);
+int fetch(git_repository *repo, int argc, char **argv);
+int index_pack(git_repository *repo, int argc, char **argv);
+
+#endif /* __COMMON_H__ */
diff --git a/examples/network/fetch.c b/examples/network/fetch.c
new file mode 100644
index 000000000..dd732f22e
--- /dev/null
+++ b/examples/network/fetch.c
@@ -0,0 +1,127 @@
+#include "common.h"
+#include <git2.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static void show_refs(git_headarray *refs)
+{
+ int i;
+ git_remote_head *head;
+
+ if(refs->len == 0)
+ puts("Everything up-to-date");
+
+ for(i = 0; i < refs->len; ++i){
+ char oid[GIT_OID_HEXSZ + 1] = {0};
+ char *havewant;
+ head = refs->heads[i];
+ git_oid_fmt(oid, &head->oid);
+ printf("%s\t%s\n", oid, head->name);
+ }
+}
+
+static int rename_packfile(char *packname, git_indexer *idx)
+{
+ char path[GIT_PATH_MAX], oid[GIT_OID_HEXSZ + 1], *slash;
+ int ret;
+
+ strcpy(path, packname);
+ slash = strrchr(path, '/');
+
+ if (!slash)
+ return GIT_EINVALIDARGS;
+
+ memset(oid, 0x0, sizeof(oid));
+ // The name of the packfile is given by it's hash which you can get
+ // with git_indexer_hash after the index has been written out to
+ // disk. Rename the packfile to its "real" name in the same
+ // directory as it was originally (libgit2 stores it in the folder
+ // where the packs go, so a rename in place is the right thing to do here
+ git_oid_fmt(oid, git_indexer_hash(idx));
+ ret = sprintf(slash + 1, "pack-%s.pack", oid);
+ if(ret < 0)
+ return GIT_EOSERR;
+
+ printf("Renaming pack to %s\n", path);
+ return rename(packname, path);
+}
+
+int fetch(git_repository *repo, int argc, char **argv)
+{
+ git_remote *remote = NULL;
+ git_config *cfg = NULL;
+ git_indexer *idx = NULL;
+ git_indexer_stats stats;
+ int error;
+ char *packname = NULL;
+
+ // Load the repository's configuration
+ error = git_repository_config(&cfg, repo, NULL, NULL);
+ if (error < GIT_SUCCESS)
+ return error;
+
+ // Get the remote and connect to it
+ printf("Fetching %s\n", argv[1]);
+ error = git_remote_get(&remote, cfg, argv[1]);
+ if (error < GIT_SUCCESS)
+ return error;
+
+ error = git_remote_connect(remote, GIT_DIR_FETCH);
+ if (error < GIT_SUCCESS)
+ return error;
+
+ // Perform the packfile negotiation. This is where the two ends
+ // figure out the minimal amount of data that should be transmitted
+ // to bring the repository up-to-date
+ error = git_remote_negotiate(remote);
+ if (error < GIT_SUCCESS)
+ return error;
+
+ // Download the packfile from the server. As we don't know its hash
+ // yet, it will get a temporary filename
+ error = git_remote_download(&packname, remote);
+ if (error < GIT_SUCCESS)
+ return error;
+
+ // No error and a NULL packname means no packfile was needed
+ if (packname != NULL) {
+ printf("The packname is %s\n", packname);
+
+ // Create a new instance indexer
+ error = git_indexer_new(&idx, packname);
+ if (error < GIT_SUCCESS)
+ return error;
+
+ // This should be run in paralel, but it'd be too complicated for the example
+ error = git_indexer_run(idx, &stats);
+ if (error < GIT_SUCCESS)
+ return error;
+
+ printf("Received %d objects\n", stats.total);
+
+ // Write the index file. The index will be stored with the
+ // correct filename
+ error = git_indexer_write(idx);
+ if (error < GIT_SUCCESS)
+ return error;
+
+ error = rename_packfile(packname, idx);
+ if (error < GIT_SUCCESS)
+ return error;
+ }
+
+ // Update the references in the remote's namespace to point to the
+ // right commits. This may be needed even if there was no packfile
+ // to download, which can happen e.g. when the branches have been
+ // changed but all the neede objects are available locally.
+ error = git_remote_update_tips(remote);
+ if (error < GIT_SUCCESS)
+ return error;
+
+ free(packname);
+ git_indexer_free(idx);
+ git_remote_free(remote);
+
+ return GIT_SUCCESS;
+}
diff --git a/examples/network/git2.c b/examples/network/git2.c
new file mode 100644
index 000000000..0468c8ace
--- /dev/null
+++ b/examples/network/git2.c
@@ -0,0 +1,57 @@
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "common.h"
+
+// This part is not strictly libgit2-dependent, but you can use this
+// as a starting point for a git-like tool
+
+struct {
+ char *name;
+ git_cb fn;
+} commands[] = {
+ {"ls-remote", ls_remote},
+ {"fetch", fetch},
+ {"index-pack", index_pack},
+ { NULL, NULL}
+};
+
+int run_command(git_cb fn, int argc, char **argv)
+{
+ int error;
+ git_repository *repo;
+
+// Before running the actual command, create an instance of the local
+// repository and pass it to the function.
+
+ error = git_repository_open(&repo, ".git");
+ if (error < GIT_SUCCESS)
+ repo = NULL;
+
+ // Run the command. If something goes wrong, print the error message to stderr
+ error = fn(repo, argc, argv);
+ if (error < GIT_SUCCESS)
+ fprintf(stderr, "Bad news:\n %s\n", git_lasterror());
+
+ if(repo)
+ git_repository_free(repo);
+
+ return !!error;
+}
+
+int main(int argc, char **argv)
+{
+ int i, error;
+
+ if (argc < 2) {
+ fprintf(stderr, "usage: %s <cmd> [repo]", argv[0]);
+ }
+
+ for (i = 0; commands[i].name != NULL; ++i) {
+ if (!strcmp(argv[1], commands[i].name))
+ return run_command(commands[i].fn, --argc, ++argv);
+ }
+
+ fprintf(stderr, "Command not found: %s\n", argv[1]);
+
+}
diff --git a/examples/network/index-pack.c b/examples/network/index-pack.c
new file mode 100644
index 000000000..671035fb7
--- /dev/null
+++ b/examples/network/index-pack.c
@@ -0,0 +1,47 @@
+#include <git2.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "common.h"
+
+// This could be run in the main loop whilst the application waits for
+// the indexing to finish in a worker thread
+int index_cb(const git_indexer_stats *stats, void *data)
+{
+ printf("\rProcessing %d of %d", stats->processed, stats->total);
+}
+
+int index_pack(git_repository *repo, int argc, char **argv)
+{
+ git_indexer *indexer;
+ git_indexer_stats stats;
+ int error;
+ char hash[GIT_OID_HEXSZ + 1] = {0};
+
+ if (argc < 2) {
+ fprintf(stderr, "I need a packfile\n");
+ return EXIT_FAILURE;
+ }
+
+ // Create a new indexer
+ error = git_indexer_new(&indexer, argv[1]);
+ if (error < GIT_SUCCESS)
+ return error;
+
+ // Index the packfile. This function can take a very long time and
+ // should be run in a worker thread.
+ error = git_indexer_run(indexer, &stats);
+ if (error < GIT_SUCCESS)
+ return error;
+
+ // Write the information out to an index file
+ error = git_indexer_write(indexer);
+
+ // Get the packfile's hash (which should become it's filename)
+ git_oid_fmt(hash, git_indexer_hash(indexer));
+ puts(hash);
+
+ git_indexer_free(indexer);
+
+ return GIT_SUCCESS;
+}
diff --git a/examples/network/ls-remote.c b/examples/network/ls-remote.c
new file mode 100644
index 000000000..77a9f215d
--- /dev/null
+++ b/examples/network/ls-remote.c
@@ -0,0 +1,104 @@
+#include <git2.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "common.h"
+
+static void show_refs(git_headarray *refs)
+{
+ int i;
+ git_remote_head *head;
+
+// Take each head that the remote has advertised, store the string
+// representation of the OID in a buffer and print it
+
+ for(i = 0; i < refs->len; ++i){
+ char oid[GIT_OID_HEXSZ + 1] = {0};
+ head = refs->heads[i];
+ git_oid_fmt(oid, &head->oid);
+ printf("%s\t%s\n", oid, head->name);
+ }
+}
+
+int use_unnamed(git_repository *repo, const char *url)
+{
+ git_remote *remote = NULL;
+ git_headarray refs;
+ int error;
+
+ // Create an instance of a remote from the URL. The transport to use
+ // is detected from the URL
+ error = git_remote_new(&remote, repo, url);
+ if (error < GIT_SUCCESS)
+ goto cleanup;
+
+ // When connecting, the underlying code needs to know wether we
+ // want to push or fetch
+ error = git_remote_connect(remote, GIT_DIR_FETCH);
+ if (error < GIT_SUCCESS)
+ goto cleanup;
+
+ // With git_remote_ls we can retrieve the advertised heads
+ error = git_remote_ls(remote, &refs);
+ if (error < GIT_SUCCESS)
+ goto cleanup;
+
+ show_refs(&refs);
+
+cleanup:
+ git_remote_free(remote);
+
+ return error;
+}
+
+int use_remote(git_repository *repo, char *name)
+{
+ git_remote *remote = NULL;
+ git_config *cfg = NULL;
+ git_headarray refs;
+ int error;
+
+ // Load the local configuration for the repository
+ error = git_repository_config(&cfg, repo, NULL, NULL);
+ if (error < GIT_SUCCESS)
+ return error;
+
+ // Find the remote by name
+ error = git_remote_get(&remote, cfg, name);
+ if (error < GIT_SUCCESS)
+ goto cleanup;
+
+ error = git_remote_connect(remote, GIT_DIR_FETCH);
+ if (error < GIT_SUCCESS)
+ goto cleanup;
+
+ error = git_remote_ls(remote, &refs);
+ if (error < GIT_SUCCESS)
+ goto cleanup;
+
+ show_refs(&refs);
+
+cleanup:
+ git_remote_free(remote);
+
+ return error;
+}
+
+// This gets called to do the work. The remote can be given either as
+// the name of a configured remote or an URL.
+
+int ls_remote(git_repository *repo, int argc, char **argv)
+{
+ git_headarray heads;
+ git_remote_head *head;
+ int error, i;
+
+ /* If there's a ':' in the name, assume it's an URL */
+ if (strchr(argv[1], ':') != NULL) {
+ error = use_unnamed(repo, argv[1]);
+ } else {
+ error = use_remote(repo, argv[1]);
+ }
+
+ return error;
+}