rugged 0.17.0.b6 → 0.17.0.b7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (158) hide show
  1. data/README.md +3 -3
  2. data/Rakefile +3 -1
  3. data/ext/rugged/rugged.c +30 -0
  4. data/ext/rugged/rugged.h +9 -0
  5. data/ext/rugged/rugged_branch.c +306 -0
  6. data/ext/rugged/rugged_config.c +16 -13
  7. data/ext/rugged/rugged_index.c +25 -0
  8. data/ext/rugged/rugged_object.c +6 -2
  9. data/ext/rugged/rugged_reference.c +11 -18
  10. data/ext/rugged/rugged_revwalk.c +1 -1
  11. data/lib/rugged.rb +1 -0
  12. data/lib/rugged/branch.rb +28 -0
  13. data/lib/rugged/commit.rb +5 -5
  14. data/lib/rugged/repository.rb +32 -7
  15. data/lib/rugged/tag.rb +5 -1
  16. data/lib/rugged/version.rb +1 -1
  17. data/test/branch_test.rb +227 -0
  18. data/test/config_test.rb +1 -1
  19. data/test/fixtures/testrepo.git/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904 +0 -0
  20. data/test/fixtures/testrepo.git/objects/a3/e05719b428a2d0ed7a55c4ce53dcc5768c6d5e +0 -0
  21. data/test/index_test.rb +31 -0
  22. data/test/index_test.rb~ +218 -0
  23. data/test/lib_test.rb +22 -0
  24. data/test/reference_test.rb +5 -3
  25. data/vendor/libgit2/Makefile.embed +1 -1
  26. data/vendor/libgit2/include/git2.h +1 -0
  27. data/vendor/libgit2/include/git2/branch.h +17 -13
  28. data/vendor/libgit2/include/git2/checkout.h +83 -22
  29. data/vendor/libgit2/include/git2/clone.h +6 -3
  30. data/vendor/libgit2/include/git2/common.h +1 -8
  31. data/vendor/libgit2/include/git2/config.h +185 -26
  32. data/vendor/libgit2/include/git2/diff.h +229 -17
  33. data/vendor/libgit2/include/git2/errors.h +39 -1
  34. data/vendor/libgit2/include/git2/ignore.h +6 -3
  35. data/vendor/libgit2/include/git2/indexer.h +1 -0
  36. data/vendor/libgit2/include/git2/merge.h +1 -1
  37. data/vendor/libgit2/include/git2/object.h +7 -4
  38. data/vendor/libgit2/include/git2/odb.h +4 -2
  39. data/vendor/libgit2/include/git2/odb_backend.h +6 -0
  40. data/vendor/libgit2/include/git2/oid.h +2 -0
  41. data/vendor/libgit2/include/git2/pack.h +89 -0
  42. data/vendor/libgit2/include/git2/refs.h +88 -0
  43. data/vendor/libgit2/include/git2/refspec.h +0 -8
  44. data/vendor/libgit2/include/git2/remote.h +34 -1
  45. data/vendor/libgit2/include/git2/repository.h +238 -6
  46. data/vendor/libgit2/include/git2/reset.h +4 -1
  47. data/vendor/libgit2/include/git2/revwalk.h +1 -1
  48. data/vendor/libgit2/include/git2/status.h +19 -14
  49. data/vendor/libgit2/include/git2/strarray.h +54 -0
  50. data/vendor/libgit2/include/git2/submodule.h +451 -45
  51. data/vendor/libgit2/include/git2/tag.h +16 -0
  52. data/vendor/libgit2/include/git2/tree.h +2 -2
  53. data/vendor/libgit2/include/git2/types.h +4 -0
  54. data/vendor/libgit2/src/amiga/map.c +4 -7
  55. data/vendor/libgit2/src/attr.c +21 -13
  56. data/vendor/libgit2/src/attr.h +3 -1
  57. data/vendor/libgit2/src/attr_file.c +14 -14
  58. data/vendor/libgit2/src/attr_file.h +6 -5
  59. data/vendor/libgit2/src/blob.c +22 -12
  60. data/vendor/libgit2/src/branch.c +62 -66
  61. data/vendor/libgit2/src/buffer.c +63 -14
  62. data/vendor/libgit2/src/buffer.h +4 -0
  63. data/vendor/libgit2/src/cache.c +5 -4
  64. data/vendor/libgit2/src/checkout.c +381 -159
  65. data/vendor/libgit2/src/clone.c +221 -94
  66. data/vendor/libgit2/src/common.h +13 -3
  67. data/vendor/libgit2/src/compress.c +53 -0
  68. data/vendor/libgit2/src/compress.h +16 -0
  69. data/vendor/libgit2/src/config.c +380 -175
  70. data/vendor/libgit2/src/config.h +2 -5
  71. data/vendor/libgit2/src/config_file.c +63 -46
  72. data/vendor/libgit2/src/config_file.h +16 -4
  73. data/vendor/libgit2/src/crlf.c +4 -3
  74. data/vendor/libgit2/src/delta.c +491 -0
  75. data/vendor/libgit2/src/delta.h +112 -0
  76. data/vendor/libgit2/src/diff.c +310 -67
  77. data/vendor/libgit2/src/diff.h +10 -1
  78. data/vendor/libgit2/src/diff_output.c +1030 -337
  79. data/vendor/libgit2/src/diff_output.h +86 -0
  80. data/vendor/libgit2/src/errors.c +10 -1
  81. data/vendor/libgit2/src/fetch.c +108 -24
  82. data/vendor/libgit2/src/filebuf.c +8 -2
  83. data/vendor/libgit2/src/fileops.c +342 -177
  84. data/vendor/libgit2/src/fileops.h +84 -7
  85. data/vendor/libgit2/src/filter.c +0 -35
  86. data/vendor/libgit2/src/filter.h +0 -12
  87. data/vendor/libgit2/src/{compat/fnmatch.c → fnmatch.c} +16 -4
  88. data/vendor/libgit2/src/{compat/fnmatch.h → fnmatch.h} +4 -3
  89. data/vendor/libgit2/src/global.c +4 -0
  90. data/vendor/libgit2/src/ignore.c +122 -23
  91. data/vendor/libgit2/src/ignore.h +1 -0
  92. data/vendor/libgit2/src/index.c +56 -10
  93. data/vendor/libgit2/src/index.h +2 -0
  94. data/vendor/libgit2/src/indexer.c +8 -9
  95. data/vendor/libgit2/src/iterator.c +244 -31
  96. data/vendor/libgit2/src/iterator.h +30 -1
  97. data/vendor/libgit2/src/message.c +1 -1
  98. data/vendor/libgit2/src/netops.c +44 -4
  99. data/vendor/libgit2/src/object.c +80 -69
  100. data/vendor/libgit2/src/object.h +39 -0
  101. data/vendor/libgit2/src/odb.c +79 -15
  102. data/vendor/libgit2/src/odb.h +20 -5
  103. data/vendor/libgit2/src/odb_pack.c +65 -33
  104. data/vendor/libgit2/src/oid.c +0 -3
  105. data/vendor/libgit2/src/pack-objects.c +1315 -0
  106. data/vendor/libgit2/src/pack-objects.h +87 -0
  107. data/vendor/libgit2/src/pack.c +36 -12
  108. data/vendor/libgit2/src/pack.h +1 -0
  109. data/vendor/libgit2/src/path.c +42 -9
  110. data/vendor/libgit2/src/path.h +14 -0
  111. data/vendor/libgit2/src/pkt.c +52 -2
  112. data/vendor/libgit2/src/pkt.h +10 -0
  113. data/vendor/libgit2/src/pool.h +11 -0
  114. data/vendor/libgit2/src/posix.h +8 -0
  115. data/vendor/libgit2/src/protocol.c +24 -2
  116. data/vendor/libgit2/src/protocol.h +4 -0
  117. data/vendor/libgit2/src/reflog.c +1 -1
  118. data/vendor/libgit2/src/refs.c +292 -124
  119. data/vendor/libgit2/src/refs.h +4 -2
  120. data/vendor/libgit2/src/refspec.c +117 -19
  121. data/vendor/libgit2/src/refspec.h +19 -0
  122. data/vendor/libgit2/src/remote.c +152 -48
  123. data/vendor/libgit2/src/remote.h +4 -1
  124. data/vendor/libgit2/src/repo_template.h +58 -0
  125. data/vendor/libgit2/src/repository.c +594 -179
  126. data/vendor/libgit2/src/repository.h +23 -22
  127. data/vendor/libgit2/src/reset.c +71 -29
  128. data/vendor/libgit2/src/revparse.c +26 -17
  129. data/vendor/libgit2/src/revwalk.c +36 -19
  130. data/vendor/libgit2/src/sha1.h +7 -0
  131. data/vendor/libgit2/src/{sha1.c → sha1/sha1.c} +0 -0
  132. data/vendor/libgit2/src/signature.c +12 -10
  133. data/vendor/libgit2/src/status.c +52 -6
  134. data/vendor/libgit2/src/submodule.c +1363 -255
  135. data/vendor/libgit2/src/submodule.h +102 -0
  136. data/vendor/libgit2/src/tag.c +42 -26
  137. data/vendor/libgit2/src/thread-utils.h +7 -7
  138. data/vendor/libgit2/src/transport.h +15 -1
  139. data/vendor/libgit2/src/transports/git.c +1 -1
  140. data/vendor/libgit2/src/transports/http.c +197 -36
  141. data/vendor/libgit2/src/tree.c +3 -3
  142. data/vendor/libgit2/src/unix/map.c +2 -0
  143. data/vendor/libgit2/src/unix/posix.h +1 -8
  144. data/vendor/libgit2/src/util.c +6 -1
  145. data/vendor/libgit2/src/util.h +7 -0
  146. data/vendor/libgit2/src/vector.c +16 -0
  147. data/vendor/libgit2/src/vector.h +1 -0
  148. data/vendor/libgit2/src/win32/dir.c +8 -21
  149. data/vendor/libgit2/src/win32/findfile.c +149 -0
  150. data/vendor/libgit2/src/win32/findfile.h +23 -0
  151. data/vendor/libgit2/src/win32/posix.h +3 -7
  152. data/vendor/libgit2/src/win32/posix_w32.c +44 -102
  153. data/vendor/libgit2/src/win32/pthread.c +68 -0
  154. data/vendor/libgit2/src/win32/pthread.h +7 -0
  155. data/vendor/libgit2/src/win32/utf-conv.c +60 -71
  156. data/vendor/libgit2/src/win32/utf-conv.h +4 -3
  157. metadata +70 -71
  158. data/vendor/libgit2/include/git2/windows.h +0 -59
@@ -144,31 +144,40 @@ int git_buf_puts(git_buf *buf, const char *string)
144
144
  int git_buf_puts_escaped(
145
145
  git_buf *buf, const char *string, const char *esc_chars, const char *esc_with)
146
146
  {
147
- const char *scan = string;
148
- size_t total = 0, esc_with_len = strlen(esc_with);
147
+ const char *scan;
148
+ size_t total = 0, esc_len = strlen(esc_with), count;
149
149
 
150
- while (*scan) {
151
- size_t count = strcspn(scan, esc_chars);
152
- total += count + 1 + esc_with_len;
153
- scan += count + 1;
150
+ if (!string)
151
+ return 0;
152
+
153
+ for (scan = string; *scan; ) {
154
+ /* count run of non-escaped characters */
155
+ count = strcspn(scan, esc_chars);
156
+ total += count;
157
+ scan += count;
158
+ /* count run of escaped characters */
159
+ count = strspn(scan, esc_chars);
160
+ total += count * (esc_len + 1);
161
+ scan += count;
154
162
  }
155
163
 
156
164
  ENSURE_SIZE(buf, buf->size + total + 1);
157
165
 
158
166
  for (scan = string; *scan; ) {
159
- size_t count = strcspn(scan, esc_chars);
167
+ count = strcspn(scan, esc_chars);
160
168
 
161
169
  memmove(buf->ptr + buf->size, scan, count);
162
170
  scan += count;
163
171
  buf->size += count;
164
172
 
165
- if (*scan) {
166
- memmove(buf->ptr + buf->size, esc_with, esc_with_len);
167
- buf->size += esc_with_len;
168
-
169
- memmove(buf->ptr + buf->size, scan, 1);
170
- scan += 1;
171
- buf->size += 1;
173
+ for (count = strspn(scan, esc_chars); count > 0; --count) {
174
+ /* copy escape sequence */
175
+ memmove(buf->ptr + buf->size, esc_with, esc_len);
176
+ buf->size += esc_len;
177
+ /* copy character to be escaped */
178
+ buf->ptr[buf->size] = *scan;
179
+ buf->size++;
180
+ scan++;
172
181
  }
173
182
  }
174
183
 
@@ -177,6 +186,46 @@ int git_buf_puts_escaped(
177
186
  return 0;
178
187
  }
179
188
 
189
+ static const char b64str[64] =
190
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
191
+
192
+ int git_buf_put_base64(git_buf *buf, const char *data, size_t len)
193
+ {
194
+ size_t extra = len % 3;
195
+ uint8_t *write, a, b, c;
196
+ const uint8_t *read = (const uint8_t *)data;
197
+
198
+ ENSURE_SIZE(buf, buf->size + 4 * ((len / 3) + !!extra) + 1);
199
+ write = (uint8_t *)&buf->ptr[buf->size];
200
+
201
+ /* convert each run of 3 bytes into 4 output bytes */
202
+ for (len -= extra; len > 0; len -= 3) {
203
+ a = *read++;
204
+ b = *read++;
205
+ c = *read++;
206
+
207
+ *write++ = b64str[a >> 2];
208
+ *write++ = b64str[(a & 0x03) << 4 | b >> 4];
209
+ *write++ = b64str[(b & 0x0f) << 2 | c >> 6];
210
+ *write++ = b64str[c & 0x3f];
211
+ }
212
+
213
+ if (extra > 0) {
214
+ a = *read++;
215
+ b = (extra > 1) ? *read++ : 0;
216
+
217
+ *write++ = b64str[a >> 2];
218
+ *write++ = b64str[(a & 0x03) << 4 | b >> 4];
219
+ *write++ = (extra > 1) ? b64str[(b & 0x0f) << 2] : '=';
220
+ *write++ = '=';
221
+ }
222
+
223
+ buf->size = ((char *)write) - buf->ptr;
224
+ buf->ptr[buf->size] = '\0';
225
+
226
+ return 0;
227
+ }
228
+
180
229
  int git_buf_vprintf(git_buf *buf, const char *format, va_list ap)
181
230
  {
182
231
  int len;
@@ -8,6 +8,7 @@
8
8
  #define INCLUDE_buffer_h__
9
9
 
10
10
  #include "common.h"
11
+ #include "git2/strarray.h"
11
12
  #include <stdarg.h>
12
13
 
13
14
  typedef struct {
@@ -154,4 +155,7 @@ bool git_buf_is_binary(const git_buf *buf);
154
155
  /* Unescape all characters in a buffer */
155
156
  void git_buf_unescape(git_buf *buf);
156
157
 
158
+ /* Write data as base64 encoded in buffer */
159
+ int git_buf_put_base64(git_buf *buf, const char *data, size_t len);
160
+
157
161
  #endif
@@ -89,12 +89,13 @@ void *git_cache_try_store(git_cache *cache, void *_entry)
89
89
  git_cached_obj_decref(node, cache->free_obj);
90
90
  cache->nodes[hash & cache->size_mask] = entry;
91
91
  }
92
+
93
+ /* increase the refcount again, because we are
94
+ * returning it to the user */
95
+ git_cached_obj_incref(entry);
96
+
92
97
  }
93
98
  git_mutex_unlock(&cache->lock);
94
99
 
95
- /* increase the refcount again, because we are
96
- * returning it to the user */
97
- git_cached_obj_incref(entry);
98
-
99
100
  return entry;
100
101
  }
@@ -11,9 +11,9 @@
11
11
  #include "git2/repository.h"
12
12
  #include "git2/refs.h"
13
13
  #include "git2/tree.h"
14
- #include "git2/commit.h"
15
14
  #include "git2/blob.h"
16
15
  #include "git2/config.h"
16
+ #include "git2/diff.h"
17
17
 
18
18
  #include "common.h"
19
19
  #include "refs.h"
@@ -22,209 +22,431 @@
22
22
  #include "filter.h"
23
23
  #include "blob.h"
24
24
 
25
- GIT_BEGIN_DECL
26
-
27
-
28
- typedef struct tree_walk_data
25
+ struct checkout_diff_data
29
26
  {
27
+ git_buf *path;
28
+ size_t workdir_len;
29
+ git_checkout_opts *checkout_opts;
30
30
  git_indexer_stats *stats;
31
- git_checkout_opts *opts;
32
- git_repository *repo;
33
- git_odb *odb;
34
- bool do_symlinks;
35
- } tree_walk_data;
31
+ git_repository *owner;
32
+ bool can_symlink;
33
+ bool found_submodules;
34
+ bool create_submodules;
35
+ int error;
36
+ };
37
+
38
+ static int buffer_to_file(
39
+ git_buf *buffer,
40
+ const char *path,
41
+ mode_t dir_mode,
42
+ int file_open_flags,
43
+ mode_t file_mode)
44
+ {
45
+ int fd, error, error_close;
36
46
 
47
+ if ((error = git_futils_mkpath2file(path, dir_mode)) < 0)
48
+ return error;
37
49
 
38
- static int blob_contents_to_link(tree_walk_data *data, git_buf *fnbuf,
39
- const git_oid *id)
40
- {
41
- int retcode = GIT_ERROR;
42
- git_blob *blob;
50
+ if ((fd = p_open(path, file_open_flags, file_mode)) < 0)
51
+ return fd;
43
52
 
44
- /* Get the link target */
45
- if (!(retcode = git_blob_lookup(&blob, data->repo, id))) {
46
- git_buf linktarget = GIT_BUF_INIT;
47
- if (!(retcode = git_blob__getbuf(&linktarget, blob))) {
48
- /* Create the link */
49
- const char *new = git_buf_cstr(&linktarget),
50
- *old = git_buf_cstr(fnbuf);
51
- retcode = data->do_symlinks
52
- ? p_symlink(new, old)
53
- : git_futils_fake_symlink(new, old);
54
- }
55
- git_buf_free(&linktarget);
56
- git_blob_free(blob);
57
- }
53
+ error = p_write(fd, git_buf_cstr(buffer), git_buf_len(buffer));
58
54
 
59
- return retcode;
60
- }
55
+ error_close = p_close(fd);
61
56
 
57
+ if (!error)
58
+ error = error_close;
62
59
 
63
- static int blob_contents_to_file(git_repository *repo, git_buf *fnbuf,
64
- const git_tree_entry *entry, tree_walk_data *data)
60
+ if (!error &&
61
+ (file_mode & 0100) != 0 &&
62
+ (error = p_chmod(path, file_mode)) < 0)
63
+ giterr_set(GITERR_OS, "Failed to set permissions on '%s'", path);
64
+
65
+ return error;
66
+ }
67
+
68
+ static int blob_content_to_file(
69
+ git_blob *blob,
70
+ const char *path,
71
+ mode_t entry_filemode,
72
+ git_checkout_opts *opts)
65
73
  {
66
- int retcode = GIT_ERROR;
67
- int fd = -1;
68
- git_buf contents = GIT_BUF_INIT;
69
- const git_oid *id = git_tree_entry_id(entry);
70
- int file_mode = data->opts->file_mode;
71
-
72
- /* Deal with pre-existing files */
73
- if (git_path_exists(git_buf_cstr(fnbuf)) &&
74
- data->opts->existing_file_action == GIT_CHECKOUT_SKIP_EXISTING)
75
- return 0;
76
-
77
- /* Allow disabling of filters */
78
- if (data->opts->disable_filters) {
79
- git_blob *blob;
80
- if (!(retcode = git_blob_lookup(&blob, repo, id))) {
81
- retcode = git_blob__getbuf(&contents, blob);
82
- git_blob_free(blob);
83
- }
84
- } else {
85
- retcode = git_filter_blob_contents(&contents, repo, id, git_buf_cstr(fnbuf));
74
+ int error = -1, nb_filters = 0;
75
+ mode_t file_mode = opts->file_mode;
76
+ bool dont_free_filtered = false;
77
+ git_buf unfiltered = GIT_BUF_INIT, filtered = GIT_BUF_INIT;
78
+ git_vector filters = GIT_VECTOR_INIT;
79
+
80
+ if (opts->disable_filters ||
81
+ (nb_filters = git_filters_load(
82
+ &filters,
83
+ git_object_owner((git_object *)blob),
84
+ path,
85
+ GIT_FILTER_TO_WORKTREE)) == 0) {
86
+
87
+ /* Create a fake git_buf from the blob raw data... */
88
+ filtered.ptr = blob->odb_object->raw.data;
89
+ filtered.size = blob->odb_object->raw.len;
90
+
91
+ /* ... and make sure it doesn't get unexpectedly freed */
92
+ dont_free_filtered = true;
93
+ }
94
+
95
+ if (nb_filters < 0)
96
+ return nb_filters;
97
+
98
+ if (nb_filters > 0) {
99
+ if ((error = git_blob__getbuf(&unfiltered, blob)) < 0)
100
+ goto cleanup;
101
+
102
+ if ((error = git_filters_apply(&filtered, &unfiltered, &filters)) < 0)
103
+ goto cleanup;
86
104
  }
87
- if (retcode < 0) goto bctf_cleanup;
88
105
 
89
106
  /* Allow overriding of file mode */
90
107
  if (!file_mode)
91
- file_mode = git_tree_entry_filemode(entry);
108
+ file_mode = entry_filemode;
109
+
110
+ error = buffer_to_file(&filtered, path, opts->dir_mode, opts->file_open_flags, file_mode);
92
111
 
93
- if ((retcode = git_futils_mkpath2file(git_buf_cstr(fnbuf), data->opts->dir_mode)) < 0)
94
- goto bctf_cleanup;
112
+ cleanup:
113
+ git_filters_free(&filters);
114
+ git_buf_free(&unfiltered);
115
+ if (!dont_free_filtered)
116
+ git_buf_free(&filtered);
95
117
 
96
- fd = p_open(git_buf_cstr(fnbuf), data->opts->file_open_flags, file_mode);
97
- if (fd < 0) goto bctf_cleanup;
118
+ return error;
119
+ }
98
120
 
99
- if (!p_write(fd, git_buf_cstr(&contents), git_buf_len(&contents)))
100
- retcode = 0;
121
+ static int blob_content_to_link(git_blob *blob, const char *path, bool can_symlink)
122
+ {
123
+ git_buf linktarget = GIT_BUF_INIT;
124
+ int error;
125
+
126
+ if ((error = git_blob__getbuf(&linktarget, blob)) < 0)
127
+ return error;
128
+
129
+ if (can_symlink)
130
+ error = p_symlink(git_buf_cstr(&linktarget), path);
101
131
  else
102
- retcode = GIT_ERROR;
103
- p_close(fd);
132
+ error = git_futils_fake_symlink(git_buf_cstr(&linktarget), path);
133
+
134
+ git_buf_free(&linktarget);
104
135
 
105
- bctf_cleanup:
106
- git_buf_free(&contents);
107
- return retcode;
136
+ return error;
108
137
  }
109
138
 
110
- static int checkout_walker(const char *path, const git_tree_entry *entry, void *payload)
139
+ static int checkout_submodule(
140
+ struct checkout_diff_data *data,
141
+ const git_diff_file *file)
111
142
  {
112
- int retcode = 0;
113
- tree_walk_data *data = (tree_walk_data*)payload;
114
- int attr = git_tree_entry_filemode(entry);
115
- git_buf fnbuf = GIT_BUF_INIT;
116
- git_buf_join_n(&fnbuf, '/', 3,
117
- git_repository_workdir(data->repo),
118
- path,
119
- git_tree_entry_name(entry));
120
-
121
- switch(git_tree_entry_type(entry))
143
+ if (git_futils_mkdir(
144
+ file->path, git_repository_workdir(data->owner),
145
+ data->checkout_opts->dir_mode, GIT_MKDIR_PATH) < 0)
146
+ return -1;
147
+
148
+ /* TODO: two cases:
149
+ * 1 - submodule already checked out, but we need to move the HEAD
150
+ * to the new OID, or
151
+ * 2 - submodule not checked out and we should recursively check it out
152
+ *
153
+ * Checkout will not execute a pull request on the submodule, but a
154
+ * clone command should probably be able to. Do we need a submodule
155
+ * callback option?
156
+ */
157
+
158
+ return 0;
159
+ }
160
+
161
+ static int checkout_blob(
162
+ struct checkout_diff_data *data,
163
+ const git_diff_file *file)
164
+ {
165
+ git_blob *blob;
166
+ int error;
167
+
168
+ git_buf_truncate(data->path, data->workdir_len);
169
+ if (git_buf_joinpath(data->path, git_buf_cstr(data->path), file->path) < 0)
170
+ return -1;
171
+
172
+ if ((error = git_blob_lookup(&blob, data->owner, &file->oid)) < 0)
173
+ return error;
174
+
175
+ if (S_ISLNK(file->mode))
176
+ error = blob_content_to_link(
177
+ blob, git_buf_cstr(data->path), data->can_symlink);
178
+ else
179
+ error = blob_content_to_file(
180
+ blob, git_buf_cstr(data->path), file->mode, data->checkout_opts);
181
+
182
+ git_blob_free(blob);
183
+
184
+ return error;
185
+ }
186
+
187
+ static int checkout_remove_the_old(
188
+ void *cb_data, const git_diff_delta *delta, float progress)
189
+ {
190
+ struct checkout_diff_data *data = cb_data;
191
+ git_checkout_opts *opts = data->checkout_opts;
192
+
193
+ GIT_UNUSED(progress);
194
+ data->stats->processed++;
195
+
196
+ if ((delta->status == GIT_DELTA_UNTRACKED &&
197
+ (opts->checkout_strategy & GIT_CHECKOUT_REMOVE_UNTRACKED) != 0) ||
198
+ (delta->status == GIT_DELTA_TYPECHANGE &&
199
+ (opts->checkout_strategy & GIT_CHECKOUT_OVERWRITE_MODIFIED) != 0))
200
+ {
201
+ data->error = git_futils_rmdir_r(
202
+ delta->new_file.path,
203
+ git_repository_workdir(data->owner),
204
+ GIT_DIRREMOVAL_FILES_AND_DIRS);
205
+ }
206
+
207
+ return data->error;
208
+ }
209
+
210
+ static int checkout_create_the_new(
211
+ void *cb_data, const git_diff_delta *delta, float progress)
212
+ {
213
+ int error = 0;
214
+ struct checkout_diff_data *data = cb_data;
215
+ git_checkout_opts *opts = data->checkout_opts;
216
+ bool do_checkout = false, do_notify = false;
217
+
218
+ GIT_UNUSED(progress);
219
+ data->stats->processed++;
220
+
221
+ if (delta->status == GIT_DELTA_MODIFIED ||
222
+ delta->status == GIT_DELTA_TYPECHANGE)
122
223
  {
123
- case GIT_OBJ_TREE:
124
- /* Nothing to do; the blob handling creates necessary directories. */
125
- break;
126
-
127
- case GIT_OBJ_COMMIT:
128
- /* Submodule */
129
- git_futils_mkpath2file(git_buf_cstr(&fnbuf), data->opts->dir_mode);
130
- retcode = p_mkdir(git_buf_cstr(&fnbuf), data->opts->dir_mode);
131
- break;
132
-
133
- case GIT_OBJ_BLOB:
134
- if (S_ISLNK(attr)) {
135
- retcode = blob_contents_to_link(data, &fnbuf,
136
- git_tree_entry_id(entry));
137
- } else {
138
- retcode = blob_contents_to_file(data->repo, &fnbuf, entry, data);
224
+ if ((opts->checkout_strategy & GIT_CHECKOUT_OVERWRITE_MODIFIED) != 0)
225
+ do_checkout = true;
226
+ else if (opts->skipped_notify_cb != NULL)
227
+ do_notify = !data->create_submodules;
228
+ }
229
+ else if (delta->status == GIT_DELTA_DELETED &&
230
+ (opts->checkout_strategy & GIT_CHECKOUT_CREATE_MISSING) != 0)
231
+ do_checkout = true;
232
+
233
+ if (do_notify) {
234
+ if (opts->skipped_notify_cb(
235
+ delta->old_file.path, &delta->old_file.oid,
236
+ delta->old_file.mode, opts->notify_payload))
237
+ {
238
+ giterr_clear();
239
+ error = GIT_EUSER;
139
240
  }
140
- break;
241
+ }
242
+
243
+ if (do_checkout) {
244
+ bool is_submodule = S_ISGITLINK(delta->old_file.mode);
141
245
 
142
- default:
143
- retcode = -1;
144
- break;
246
+ if (is_submodule)
247
+ data->found_submodules = true;
248
+
249
+ if (!is_submodule && !data->create_submodules)
250
+ error = checkout_blob(data, &delta->old_file);
251
+
252
+ else if (is_submodule && data->create_submodules)
253
+ error = checkout_submodule(data, &delta->old_file);
145
254
  }
146
255
 
147
- git_buf_free(&fnbuf);
148
- data->stats->processed++;
149
- return retcode;
150
- }
256
+ if (error)
257
+ data->error = error;
151
258
 
259
+ return error;
260
+ }
152
261
 
153
- int git_checkout_head(git_repository *repo, git_checkout_opts *opts, git_indexer_stats *stats)
262
+ static int retrieve_symlink_capabilities(git_repository *repo, bool *can_symlink)
154
263
  {
155
- int retcode = GIT_ERROR;
156
- git_indexer_stats dummy_stats;
157
- git_checkout_opts default_opts = {0};
158
- git_tree *tree;
159
- tree_walk_data payload;
160
264
  git_config *cfg;
265
+ int error;
161
266
 
162
- assert(repo);
163
- if (!opts) opts = &default_opts;
164
- if (!stats) stats = &dummy_stats;
267
+ if (git_repository_config__weakptr(&cfg, repo) < 0)
268
+ return -1;
269
+
270
+ error = git_config_get_bool((int *)can_symlink, cfg, "core.symlinks");
271
+
272
+ /*
273
+ * When no "core.symlinks" entry is found in any of the configuration
274
+ * store (local, global or system), default value is "true".
275
+ */
276
+ if (error == GIT_ENOTFOUND) {
277
+ *can_symlink = true;
278
+ error = 0;
279
+ }
280
+
281
+ return error;
282
+ }
283
+
284
+ static void normalize_options(git_checkout_opts *normalized, git_checkout_opts *proposed)
285
+ {
286
+ assert(normalized);
287
+
288
+ if (!proposed)
289
+ memset(normalized, 0, sizeof(git_checkout_opts));
290
+ else
291
+ memmove(normalized, proposed, sizeof(git_checkout_opts));
165
292
 
166
293
  /* Default options */
167
- if (!opts->existing_file_action)
168
- opts->existing_file_action = GIT_CHECKOUT_OVERWRITE_EXISTING;
294
+ if (!normalized->checkout_strategy)
295
+ normalized->checkout_strategy = GIT_CHECKOUT_DEFAULT;
296
+
169
297
  /* opts->disable_filters is false by default */
170
- if (!opts->dir_mode) opts->dir_mode = GIT_DIR_MODE;
171
- if (!opts->file_open_flags)
172
- opts->file_open_flags = O_CREAT | O_TRUNC | O_WRONLY;
298
+ if (!normalized->dir_mode)
299
+ normalized->dir_mode = GIT_DIR_MODE;
173
300
 
174
- if (git_repository_is_bare(repo)) {
175
- giterr_set(GITERR_INVALID, "Checkout is not allowed for bare repositories");
176
- return GIT_ERROR;
177
- }
301
+ if (!normalized->file_open_flags)
302
+ normalized->file_open_flags = O_CREAT | O_TRUNC | O_WRONLY;
303
+ }
178
304
 
179
- /* Determine if symlinks should be handled */
180
- if (!git_repository_config(&cfg, repo)) {
181
- int temp = true;
182
- if (!git_config_get_bool(&temp, cfg, "core.symlinks")) {
183
- payload.do_symlinks = !!temp;
184
- }
185
- git_config_free(cfg);
186
- }
305
+ int git_checkout_index(
306
+ git_repository *repo,
307
+ git_checkout_opts *opts,
308
+ git_indexer_stats *stats)
309
+ {
310
+ git_diff_list *diff = NULL;
311
+ git_indexer_stats dummy_stats;
187
312
 
188
- stats->total = stats->processed = 0;
189
- payload.stats = stats;
190
- payload.opts = opts;
191
- payload.repo = repo;
192
- if (git_repository_odb(&payload.odb, repo) < 0) return GIT_ERROR;
193
-
194
- if (!git_repository_head_tree(&tree, repo)) {
195
- git_index *idx;
196
- if (!(retcode = git_repository_index(&idx, repo))) {
197
- if (!(retcode = git_index_read_tree(idx, tree, stats))) {
198
- git_index_write(idx);
199
- retcode = git_tree_walk(tree, checkout_walker, GIT_TREEWALK_POST, &payload);
200
- }
201
- git_index_free(idx);
202
- }
203
- git_tree_free(tree);
313
+ git_diff_options diff_opts = {0};
314
+ git_checkout_opts checkout_opts;
315
+
316
+ struct checkout_diff_data data;
317
+ git_buf workdir = GIT_BUF_INIT;
318
+
319
+ int error;
320
+
321
+ assert(repo);
322
+
323
+ if ((error = git_repository__ensure_not_bare(repo, "checkout")) < 0)
324
+ return error;
325
+
326
+ diff_opts.flags =
327
+ GIT_DIFF_INCLUDE_UNTRACKED |
328
+ GIT_DIFF_INCLUDE_TYPECHANGE |
329
+ GIT_DIFF_SKIP_BINARY_CHECK;
330
+
331
+ if (opts && opts->paths.count > 0)
332
+ diff_opts.pathspec = opts->paths;
333
+
334
+ if ((error = git_diff_workdir_to_index(repo, &diff_opts, &diff)) < 0)
335
+ goto cleanup;
336
+
337
+ if ((error = git_buf_puts(&workdir, git_repository_workdir(repo))) < 0)
338
+ goto cleanup;
339
+
340
+ normalize_options(&checkout_opts, opts);
341
+
342
+ if (!stats)
343
+ stats = &dummy_stats;
344
+
345
+ stats->processed = 0;
346
+ /* total based on 3 passes, but it might be 2 if no submodules */
347
+ stats->total = (unsigned int)git_diff_num_deltas(diff) * 3;
348
+
349
+ memset(&data, 0, sizeof(data));
350
+
351
+ data.path = &workdir;
352
+ data.workdir_len = git_buf_len(&workdir);
353
+ data.checkout_opts = &checkout_opts;
354
+ data.stats = stats;
355
+ data.owner = repo;
356
+
357
+ if ((error = retrieve_symlink_capabilities(repo, &data.can_symlink)) < 0)
358
+ goto cleanup;
359
+
360
+ /* Checkout is best performed with three passes through the diff.
361
+ *
362
+ * 1. First do removes, because we iterate in alphabetical order, thus
363
+ * a new untracked directory will end up sorted *after* a blob that
364
+ * should be checked out with the same name.
365
+ * 2. Then checkout all blobs.
366
+ * 3. Then checkout all submodules in case a new .gitmodules blob was
367
+ * checked out during pass #2.
368
+ */
369
+
370
+ if (!(error = git_diff_foreach(
371
+ diff, &data, checkout_remove_the_old, NULL, NULL)) &&
372
+ !(error = git_diff_foreach(
373
+ diff, &data, checkout_create_the_new, NULL, NULL)) &&
374
+ data.found_submodules)
375
+ {
376
+ data.create_submodules = true;
377
+ error = git_diff_foreach(
378
+ diff, &data, checkout_create_the_new, NULL, NULL);
204
379
  }
205
380
 
206
- git_odb_free(payload.odb);
207
- return retcode;
208
- }
381
+ stats->processed = stats->total;
209
382
 
383
+ cleanup:
384
+ if (error == GIT_EUSER)
385
+ error = (data.error != 0) ? data.error : -1;
210
386
 
211
- int git_checkout_reference(git_reference *ref,
212
- git_checkout_opts *opts,
213
- git_indexer_stats *stats)
387
+ git_diff_list_free(diff);
388
+ git_buf_free(&workdir);
389
+
390
+ return error;
391
+ }
392
+
393
+ int git_checkout_tree(
394
+ git_repository *repo,
395
+ git_object *treeish,
396
+ git_checkout_opts *opts,
397
+ git_indexer_stats *stats)
214
398
  {
215
- git_repository *repo= git_reference_owner(ref);
216
- git_reference *head = NULL;
217
- int retcode = GIT_ERROR;
399
+ git_index *index = NULL;
400
+ git_tree *tree = NULL;
218
401
 
219
- if ((retcode = git_reference_create_symbolic(&head, repo, GIT_HEAD_FILE,
220
- git_reference_name(ref), true)) < 0)
221
- return retcode;
402
+ int error;
222
403
 
223
- retcode = git_checkout_head(git_reference_owner(ref), opts, stats);
404
+ assert(repo && treeish);
224
405
 
225
- git_reference_free(head);
226
- return retcode;
406
+ if (git_object_peel((git_object **)&tree, treeish, GIT_OBJ_TREE) < 0) {
407
+ giterr_set(GITERR_INVALID, "Provided treeish cannot be peeled into a tree.");
408
+ return GIT_ERROR;
409
+ }
410
+
411
+ if ((error = git_repository_index(&index, repo)) < 0)
412
+ goto cleanup;
413
+
414
+ if ((error = git_index_read_tree(index, tree, NULL)) < 0)
415
+ goto cleanup;
416
+
417
+ if ((error = git_index_write(index)) < 0)
418
+ goto cleanup;
419
+
420
+ error = git_checkout_index(repo, opts, stats);
421
+
422
+ cleanup:
423
+ git_index_free(index);
424
+ git_tree_free(tree);
425
+ return error;
227
426
  }
228
427
 
428
+ int git_checkout_head(
429
+ git_repository *repo,
430
+ git_checkout_opts *opts,
431
+ git_indexer_stats *stats)
432
+ {
433
+ git_reference *head;
434
+ int error;
435
+ git_object *tree = NULL;
436
+
437
+ assert(repo);
438
+
439
+ if ((error = git_repository_head(&head, repo)) < 0)
440
+ return error;
441
+
442
+ if ((error = git_reference_peel(&tree, head, GIT_OBJ_TREE)) < 0)
443
+ goto cleanup;
444
+
445
+ error = git_checkout_tree(repo, tree, opts, stats);
229
446
 
230
- GIT_END_DECL
447
+ cleanup:
448
+ git_reference_free(head);
449
+ git_object_free(tree);
450
+
451
+ return error;
452
+ }