rugged 0.17.0.b6 → 0.17.0.b7

Sign up to get free protection for your applications and to get access to all the features.
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
+ }