Commit 24b0d3d56ea25f8cb0acd425392d74300bc85a61

Ben Straub 2012-06-25T16:02:16

Checkout: read blob objects to file. Properly handling file modes. Still needs line- ending transformations.

diff --git a/src/checkout.c b/src/checkout.c
index cd4bc5a..ff4a8f8 100644
--- a/src/checkout.c
+++ b/src/checkout.c
@@ -12,10 +12,12 @@
 #include "git2/refs.h"
 #include "git2/tree.h"
 #include "git2/commit.h"
+#include "git2/blob.h"
 
 #include "common.h"
 #include "refs.h"
 #include "buffer.h"
+#include "repository.h"
 
 GIT_BEGIN_DECL
 
@@ -46,6 +48,7 @@ static int get_head_tree(git_tree **out, git_repository *repo)
 typedef struct tree_walk_data
 {
    git_indexer_stats *stats;
+   git_repository *repo;
 } tree_walk_data;
 
 
@@ -57,18 +60,51 @@ static int count_walker(const char *path, git_tree_entry *entry, void *payload)
    return 0;
 }
 
+static int blob_contents_to_file(git_repository *repo, git_buf *fnbuf, const git_oid *id, int mode)
+{
+   int retcode = GIT_ERROR;
+
+   git_blob *blob;
+   if (!git_blob_lookup(&blob, repo, id)) {
+      const void *contents = git_blob_rawcontent(blob);
+      size_t len = git_blob_rawsize(blob);
+
+      /* TODO: line-ending smudging */
+
+      int fd = git_futils_creat_withpath(git_buf_cstr(fnbuf),
+                                         GIT_DIR_MODE, mode);
+      if (fd >= 0) {
+         retcode = (!p_write(fd, contents, len)) ? 0 : GIT_ERROR;
+         p_close(fd);
+      }
+
+      git_blob_free(blob);
+   }
+
+   return retcode;
+}
+
 static int checkout_walker(const char *path, git_tree_entry *entry, void *payload)
 {
    int retcode = 0;
    tree_walk_data *data = (tree_walk_data*)payload;
+   int attr = git_tree_entry_attributes(entry);
 
    switch(git_tree_entry_type(entry)) {
    case GIT_OBJ_TREE:
-      /* TODO: mkdir */
+      /* TODO: mkdir? */
       break;
 
    case GIT_OBJ_BLOB:
-      /* TODO: create/populate file */
+      {
+         git_buf fnbuf = GIT_BUF_INIT;
+         git_buf_join_n(&fnbuf, '/', 3,
+                        git_repository_workdir(data->repo),
+                        path,
+                        git_tree_entry_name(entry));
+         retcode = blob_contents_to_file(data->repo, &fnbuf, git_tree_entry_id(entry), attr);
+         git_buf_free(&fnbuf);
+      }
       break;
 
    default:
@@ -95,6 +131,7 @@ int git_checkout_force(git_repository *repo, git_indexer_stats *stats)
 
    stats->total = stats->processed = 0;
    payload.stats = stats;
+   payload.repo = repo;
 
    if (!get_head_tree(&tree, repo)) {
       /* Count all the tree nodes for progress information */