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
@@ -0,0 +1,86 @@
1
+ /*
2
+ * Copyright (C) 2009-2012 the libgit2 contributors
3
+ *
4
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
5
+ * a Linking Exception. For full terms see the included COPYING file.
6
+ */
7
+ #ifndef INCLUDE_diff_output_h__
8
+ #define INCLUDE_diff_output_h__
9
+
10
+ #include "git2/blob.h"
11
+ #include "diff.h"
12
+ #include "map.h"
13
+ #include "xdiff/xdiff.h"
14
+
15
+ #define MAX_DIFF_FILESIZE 0x20000000
16
+
17
+ enum {
18
+ GIT_DIFF_PATCH_ALLOCATED = (1 << 0),
19
+ GIT_DIFF_PATCH_PREPPED = (1 << 1),
20
+ GIT_DIFF_PATCH_LOADED = (1 << 2),
21
+ GIT_DIFF_PATCH_DIFFABLE = (1 << 3),
22
+ GIT_DIFF_PATCH_DIFFED = (1 << 4),
23
+ };
24
+
25
+ /* context for performing diffs */
26
+ typedef struct {
27
+ git_repository *repo;
28
+ git_diff_list *diff;
29
+ const git_diff_options *opts;
30
+ git_diff_file_fn file_cb;
31
+ git_diff_hunk_fn hunk_cb;
32
+ git_diff_data_fn data_cb;
33
+ void *cb_data;
34
+ int cb_error;
35
+ git_diff_range cb_range;
36
+ xdemitconf_t xdiff_config;
37
+ xpparam_t xdiff_params;
38
+ } diff_context;
39
+
40
+ /* cached information about a single span in a diff */
41
+ typedef struct diff_patch_line diff_patch_line;
42
+ struct diff_patch_line {
43
+ const char *ptr;
44
+ size_t len;
45
+ int lines, oldno, newno;
46
+ char origin;
47
+ };
48
+
49
+ /* cached information about a hunk in a diff */
50
+ typedef struct diff_patch_hunk diff_patch_hunk;
51
+ struct diff_patch_hunk {
52
+ git_diff_range range;
53
+ char header[128];
54
+ size_t header_len;
55
+ size_t line_start;
56
+ size_t line_count;
57
+ };
58
+
59
+ struct git_diff_patch {
60
+ git_refcount rc;
61
+ git_diff_list *diff; /* for refcount purposes, maybe NULL for blob diffs */
62
+ git_diff_delta *delta;
63
+ diff_context *ctxt; /* only valid while generating patch */
64
+ git_iterator_type_t old_src;
65
+ git_iterator_type_t new_src;
66
+ git_blob *old_blob;
67
+ git_blob *new_blob;
68
+ git_map old_data;
69
+ git_map new_data;
70
+ uint32_t flags;
71
+ diff_patch_hunk *hunks;
72
+ size_t hunks_asize, hunks_size;
73
+ diff_patch_line *lines;
74
+ size_t lines_asize, lines_size;
75
+ };
76
+
77
+ /* context for performing diff on a single delta */
78
+ typedef struct {
79
+ git_diff_patch *patch;
80
+ uint32_t prepped : 1;
81
+ uint32_t loaded : 1;
82
+ uint32_t diffable : 1;
83
+ uint32_t diffed : 1;
84
+ } diff_delta_context;
85
+
86
+ #endif
@@ -94,7 +94,11 @@ void giterr_set(int error_class, const char *string, ...)
94
94
 
95
95
  void giterr_set_str(int error_class, const char *string)
96
96
  {
97
- char *message = git__strdup(string);
97
+ char *message;
98
+
99
+ assert(string);
100
+
101
+ message = git__strdup(string);
98
102
 
99
103
  if (message)
100
104
  set_error(error_class, message);
@@ -110,6 +114,11 @@ void giterr_set_regex(const regex_t *regex, int error_code)
110
114
  void giterr_clear(void)
111
115
  {
112
116
  GIT_GLOBAL->last_error = NULL;
117
+
118
+ errno = 0;
119
+ #ifdef GIT_WIN32
120
+ SetLastError(0);
121
+ #endif
113
122
  }
114
123
 
115
124
  const git_error *giterr_last(void)
@@ -21,7 +21,7 @@
21
21
 
22
22
  struct filter_payload {
23
23
  git_remote *remote;
24
- const git_refspec *spec;
24
+ const git_refspec *spec, *tagspec;
25
25
  git_odb *odb;
26
26
  int found_head;
27
27
  };
@@ -29,18 +29,21 @@ struct filter_payload {
29
29
  static int filter_ref__cb(git_remote_head *head, void *payload)
30
30
  {
31
31
  struct filter_payload *p = payload;
32
+ int match = 0;
32
33
 
33
- if (!p->found_head && strcmp(head->name, GIT_HEAD_FILE) == 0) {
34
+ if (!git_reference_is_valid_name(head->name))
35
+ return 0;
36
+
37
+ if (!p->found_head && strcmp(head->name, GIT_HEAD_FILE) == 0)
34
38
  p->found_head = 1;
35
- } else {
36
- /* If it doesn't match the refpec, we don't want it */
37
- if (!git_refspec_src_matches(p->spec, head->name))
38
- return 0;
39
+ else if (git_refspec_src_matches(p->spec, head->name))
40
+ match = 1;
41
+ else if (p->remote->download_tags == GIT_REMOTE_DOWNLOAD_TAGS_ALL &&
42
+ git_refspec_src_matches(p->tagspec, head->name))
43
+ match = 1;
39
44
 
40
- /* Don't even try to ask for the annotation target */
41
- if (!git__suffixcmp(head->name, "^{}"))
42
- return 0;
43
- }
45
+ if (!match)
46
+ return 0;
44
47
 
45
48
  /* If we have the object, mark it so we don't ask for it */
46
49
  if (git_odb_exists(p->odb, &head->oid))
@@ -54,8 +57,12 @@ static int filter_ref__cb(git_remote_head *head, void *payload)
54
57
  static int filter_wants(git_remote *remote)
55
58
  {
56
59
  struct filter_payload p;
60
+ git_refspec tagspec;
61
+ int error = -1;
57
62
 
58
63
  git_vector_clear(&remote->refs);
64
+ if (git_refspec__parse(&tagspec, GIT_REFSPEC_TAGS, true) < 0)
65
+ return error;
59
66
 
60
67
  /*
61
68
  * The fetch refspec can be NULL, and what this means is that the
@@ -64,13 +71,19 @@ static int filter_wants(git_remote *remote)
64
71
  * HEAD, which will be stored in FETCH_HEAD after the fetch.
65
72
  */
66
73
  p.spec = git_remote_fetchspec(remote);
74
+ p.tagspec = &tagspec;
67
75
  p.found_head = 0;
68
76
  p.remote = remote;
69
77
 
70
78
  if (git_repository_odb__weakptr(&p.odb, remote->repo) < 0)
71
- return -1;
79
+ goto cleanup;
80
+
81
+ error = git_remote_ls(remote, filter_ref__cb, &p);
72
82
 
73
- return git_remote_ls(remote, filter_ref__cb, &p);
83
+ cleanup:
84
+ git_refspec__free(&tagspec);
85
+
86
+ return error;
74
87
  }
75
88
 
76
89
  /* Wait until we get an ack from the */
@@ -139,7 +152,7 @@ int git_fetch_negotiate(git_remote *remote)
139
152
  gitno_buffer *buf = &t->buffer;
140
153
  git_buf data = GIT_BUF_INIT;
141
154
  git_revwalk *walk = NULL;
142
- int error, pkt_type;
155
+ int error = -1, pkt_type;
143
156
  unsigned int i;
144
157
  git_oid oid;
145
158
 
@@ -177,6 +190,12 @@ int git_fetch_negotiate(git_remote *remote)
177
190
  git_pkt_buffer_have(&oid, &data);
178
191
  i++;
179
192
  if (i % 20 == 0) {
193
+ if (t->cancel.val) {
194
+ giterr_set(GITERR_NET, "The fetch was cancelled by the user");
195
+ error = GIT_EUSER;
196
+ goto on_error;
197
+ }
198
+
180
199
  git_pkt_buffer_flush(&data);
181
200
  if (git_buf_oom(&data))
182
201
  goto on_error;
@@ -221,7 +240,7 @@ int git_fetch_negotiate(git_remote *remote)
221
240
  }
222
241
  }
223
242
 
224
- if (error < 0 && error != GIT_REVWALKOVER)
243
+ if (error < 0 && error != GIT_ITEROVER)
225
244
  goto on_error;
226
245
 
227
246
  /* Tell the other end that we're done negotiating */
@@ -241,6 +260,11 @@ int git_fetch_negotiate(git_remote *remote)
241
260
  }
242
261
 
243
262
  git_pkt_buffer_done(&data);
263
+ if (t->cancel.val) {
264
+ giterr_set(GITERR_NET, "The fetch was cancelled by the user");
265
+ error = GIT_EUSER;
266
+ goto on_error;
267
+ }
244
268
  if (t->negotiation_step(t, data.ptr, data.size) < 0)
245
269
  goto on_error;
246
270
 
@@ -275,7 +299,7 @@ int git_fetch_negotiate(git_remote *remote)
275
299
  on_error:
276
300
  git_revwalk_free(walk);
277
301
  git_buf_free(&data);
278
- return -1;
302
+ return error;
279
303
  }
280
304
 
281
305
  int git_fetch_download_pack(git_remote *remote, git_off_t *bytes, git_indexer_stats *stats)
@@ -292,6 +316,33 @@ int git_fetch_download_pack(git_remote *remote, git_off_t *bytes, git_indexer_st
292
316
 
293
317
  }
294
318
 
319
+ static int no_sideband(git_transport *t, git_indexer_stream *idx, gitno_buffer *buf, git_off_t *bytes, git_indexer_stats *stats)
320
+ {
321
+ int recvd;
322
+
323
+ do {
324
+ if (t->cancel.val) {
325
+ giterr_set(GITERR_NET, "The fetch was cancelled by the user");
326
+ return GIT_EUSER;
327
+ }
328
+
329
+ if (git_indexer_stream_add(idx, buf->data, buf->offset, stats) < 0)
330
+ return -1;
331
+
332
+ gitno_consume_n(buf, buf->offset);
333
+
334
+ if ((recvd = gitno_recv(buf)) < 0)
335
+ return -1;
336
+
337
+ *bytes += recvd;
338
+ } while(recvd > 0);
339
+
340
+ if (git_indexer_stream_finalize(idx, stats))
341
+ return -1;
342
+
343
+ return 0;
344
+ }
345
+
295
346
  /* Receiving data from a socket and storing it is pretty much the same for git and HTTP */
296
347
  int git_fetch__download_pack(
297
348
  git_transport *t,
@@ -299,10 +350,10 @@ int git_fetch__download_pack(
299
350
  git_off_t *bytes,
300
351
  git_indexer_stats *stats)
301
352
  {
302
- int recvd;
303
353
  git_buf path = GIT_BUF_INIT;
304
354
  gitno_buffer *buf = &t->buffer;
305
355
  git_indexer_stream *idx = NULL;
356
+ int error = -1;
306
357
 
307
358
  if (git_buf_joinpath(&path, git_repository_path(repo), "objects/pack") < 0)
308
359
  return -1;
@@ -314,19 +365,52 @@ int git_fetch__download_pack(
314
365
  memset(stats, 0, sizeof(git_indexer_stats));
315
366
  *bytes = 0;
316
367
 
317
- do {
318
- if (git_indexer_stream_add(idx, buf->data, buf->offset, stats) < 0)
368
+ /*
369
+ * If the remote doesn't support the side-band, we can feed
370
+ * the data directly to the indexer. Otherwise, we need to
371
+ * check which one belongs there.
372
+ */
373
+ if (!t->caps.side_band && !t->caps.side_band_64k) {
374
+ if (no_sideband(t, idx, buf, bytes, stats) < 0)
319
375
  goto on_error;
320
376
 
321
- gitno_consume_n(buf, buf->offset);
377
+ git_indexer_stream_free(idx);
378
+ return 0;
379
+ }
322
380
 
323
- if ((recvd = gitno_recv(buf)) < 0)
381
+ do {
382
+ git_pkt *pkt;
383
+
384
+ if (t->cancel.val) {
385
+ giterr_set(GITERR_NET, "The fetch was cancelled by the user");
386
+ error = GIT_EUSER;
324
387
  goto on_error;
388
+ }
325
389
 
326
- *bytes += recvd;
327
- } while(recvd > 0);
390
+ if (recv_pkt(&pkt, buf) < 0)
391
+ goto on_error;
328
392
 
329
- if (git_indexer_stream_finalize(idx, stats))
393
+ if (pkt->type == GIT_PKT_PROGRESS) {
394
+ if (t->progress_cb) {
395
+ git_pkt_progress *p = (git_pkt_progress *) pkt;
396
+ t->progress_cb(p->data, p->len, t->cb_data);
397
+ }
398
+ git__free(pkt);
399
+ } else if (pkt->type == GIT_PKT_DATA) {
400
+ git_pkt_data *p = (git_pkt_data *) pkt;
401
+ *bytes += p->len;
402
+ if (git_indexer_stream_add(idx, p->data, p->len, stats) < 0)
403
+ goto on_error;
404
+
405
+ git__free(pkt);
406
+ } else if (pkt->type == GIT_PKT_FLUSH) {
407
+ /* A flush indicates the end of the packfile */
408
+ git__free(pkt);
409
+ break;
410
+ }
411
+ } while (1);
412
+
413
+ if (git_indexer_stream_finalize(idx, stats) < 0)
330
414
  goto on_error;
331
415
 
332
416
  git_indexer_stream_free(idx);
@@ -335,7 +419,7 @@ int git_fetch__download_pack(
335
419
  on_error:
336
420
  git_buf_free(&path);
337
421
  git_indexer_stream_free(idx);
338
- return -1;
422
+ return error;
339
423
  }
340
424
 
341
425
  int git_fetch_setup_walk(git_revwalk **out, git_repository *repo)
@@ -50,6 +50,7 @@ static int lock_file(git_filebuf *file, int flags)
50
50
  if (flags & GIT_FILEBUF_FORCE)
51
51
  p_unlink(file->path_lock);
52
52
  else {
53
+ giterr_clear(); /* actual OS error code just confuses */
53
54
  giterr_set(GITERR_OS,
54
55
  "Failed to lock file '%s' for writing", file->path_lock);
55
56
  return -1;
@@ -72,7 +73,7 @@ static int lock_file(git_filebuf *file, int flags)
72
73
  if ((flags & GIT_FILEBUF_APPEND) && git_path_exists(file->path_original) == true) {
73
74
  git_file source;
74
75
  char buffer[2048];
75
- size_t read_bytes;
76
+ ssize_t read_bytes;
76
77
 
77
78
  source = p_open(file->path_original, O_RDONLY);
78
79
  if (source < 0) {
@@ -82,13 +83,18 @@ static int lock_file(git_filebuf *file, int flags)
82
83
  return -1;
83
84
  }
84
85
 
85
- while ((read_bytes = p_read(source, buffer, 2048)) > 0) {
86
+ while ((read_bytes = p_read(source, buffer, sizeof(buffer))) > 0) {
86
87
  p_write(file->fd, buffer, read_bytes);
87
88
  if (file->digest)
88
89
  git_hash_update(file->digest, buffer, read_bytes);
89
90
  }
90
91
 
91
92
  p_close(source);
93
+
94
+ if (read_bytes < 0) {
95
+ giterr_set(GITERR_OS, "Failed to read file '%s'", file->path_original);
96
+ return -1;
97
+ }
92
98
  }
93
99
 
94
100
  return 0;
@@ -7,22 +7,14 @@
7
7
  #include "common.h"
8
8
  #include "fileops.h"
9
9
  #include <ctype.h>
10
+ #if GIT_WIN32
11
+ #include "win32/findfile.h"
12
+ #endif
10
13
 
11
14
  int git_futils_mkpath2file(const char *file_path, const mode_t mode)
12
15
  {
13
- int result = 0;
14
- git_buf target_folder = GIT_BUF_INIT;
15
-
16
- if (git_path_dirname_r(&target_folder, file_path) < 0)
17
- return -1;
18
-
19
- /* Does the containing folder exist? */
20
- if (git_path_isdir(target_folder.ptr) == false)
21
- /* Let's create the tree structure */
22
- result = git_futils_mkdir_r(target_folder.ptr, NULL, mode);
23
-
24
- git_buf_free(&target_folder);
25
- return result;
16
+ return git_futils_mkdir(
17
+ file_path, NULL, mode, GIT_MKDIR_PATH | GIT_MKDIR_SKIP_LAST);
26
18
  }
27
19
 
28
20
  int git_futils_mktmp(git_buf *path_out, const char *filename)
@@ -65,11 +57,10 @@ int git_futils_creat_locked(const char *path, const mode_t mode)
65
57
  int fd;
66
58
 
67
59
  #ifdef GIT_WIN32
68
- wchar_t* buf;
60
+ wchar_t buf[GIT_WIN_PATH];
69
61
 
70
- buf = gitwin_to_utf16(path);
62
+ git__utf8_to_16(buf, GIT_WIN_PATH, path);
71
63
  fd = _wopen(buf, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_EXCL, mode);
72
- git__free(buf);
73
64
  #else
74
65
  fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_EXCL, mode);
75
66
  #endif
@@ -127,10 +118,33 @@ mode_t git_futils_canonical_mode(mode_t raw_mode)
127
118
  return 0;
128
119
  }
129
120
 
130
- int git_futils_readbuffer_updated(git_buf *buf, const char *path, time_t *mtime, int *updated)
121
+ int git_futils_readbuffer_fd(git_buf *buf, git_file fd, size_t len)
122
+ {
123
+ ssize_t read_size;
124
+
125
+ git_buf_clear(buf);
126
+
127
+ if (git_buf_grow(buf, len + 1) < 0)
128
+ return -1;
129
+
130
+ /* p_read loops internally to read len bytes */
131
+ read_size = p_read(fd, buf->ptr, len);
132
+
133
+ if (read_size != (ssize_t)len) {
134
+ giterr_set(GITERR_OS, "Failed to read descriptor");
135
+ return -1;
136
+ }
137
+
138
+ buf->ptr[read_size] = '\0';
139
+ buf->size = read_size;
140
+
141
+ return 0;
142
+ }
143
+
144
+ int git_futils_readbuffer_updated(
145
+ git_buf *buf, const char *path, time_t *mtime, int *updated)
131
146
  {
132
147
  git_file fd;
133
- size_t len;
134
148
  struct stat st;
135
149
 
136
150
  assert(buf && path && *path);
@@ -159,30 +173,11 @@ int git_futils_readbuffer_updated(git_buf *buf, const char *path, time_t *mtime,
159
173
  if (mtime != NULL)
160
174
  *mtime = st.st_mtime;
161
175
 
162
- len = (size_t) st.st_size;
163
-
164
- git_buf_clear(buf);
165
-
166
- if (git_buf_grow(buf, len + 1) < 0) {
176
+ if (git_futils_readbuffer_fd(buf, fd, (size_t)st.st_size) < 0) {
167
177
  p_close(fd);
168
178
  return -1;
169
179
  }
170
180
 
171
- buf->ptr[len] = '\0';
172
-
173
- while (len > 0) {
174
- ssize_t read_size = p_read(fd, buf->ptr, len);
175
-
176
- if (read_size < 0) {
177
- p_close(fd);
178
- giterr_set(GITERR_OS, "Failed to read descriptor for '%s'", path);
179
- return -1;
180
- }
181
-
182
- len -= read_size;
183
- buf->size += read_size;
184
- }
185
-
186
181
  p_close(fd);
187
182
 
188
183
  if (updated != NULL)
@@ -239,76 +234,98 @@ void git_futils_mmap_free(git_map *out)
239
234
  p_munmap(out);
240
235
  }
241
236
 
242
- int git_futils_mkdir_r(const char *path, const char *base, const mode_t mode)
237
+ int git_futils_mkdir(
238
+ const char *path,
239
+ const char *base,
240
+ mode_t mode,
241
+ uint32_t flags)
243
242
  {
244
243
  git_buf make_path = GIT_BUF_INIT;
245
- size_t start = 0;
246
- char *pp, *sp;
247
- bool failed = false;
248
-
249
- if (base != NULL) {
250
- /*
251
- * when a base is being provided, it is supposed to already exist.
252
- * Therefore, no attempt is being made to recursively create this leading path
253
- * segment. It's just skipped. */
254
- start = strlen(base);
255
- if (git_buf_joinpath(&make_path, base, path) < 0)
256
- return -1;
257
- } else {
258
- int root_path_offset;
244
+ ssize_t root = 0;
245
+ char lastch, *tail;
259
246
 
260
- if (git_buf_puts(&make_path, path) < 0)
261
- return -1;
247
+ /* build path and find "root" where we should start calling mkdir */
248
+ if (git_path_join_unrooted(&make_path, path, base, &root) < 0)
249
+ return -1;
262
250
 
263
- root_path_offset = git_path_root(make_path.ptr);
264
- if (root_path_offset > 0) {
265
- /*
266
- * On Windows, will skip the drive name (eg. C: or D:)
267
- * or the leading part of a network path (eg. //computer_name ) */
268
- start = root_path_offset;
269
- }
251
+ if (make_path.size == 0) {
252
+ giterr_set(GITERR_OS, "Attempt to create empty path");
253
+ goto fail;
270
254
  }
271
255
 
272
- pp = make_path.ptr + start;
273
-
274
- while (!failed && (sp = strchr(pp, '/')) != NULL) {
275
- if (sp != pp && git_path_isdir(make_path.ptr) == false) {
276
- *sp = 0;
277
-
278
- /* Do not choke while trying to recreate an existing directory */
279
- if (p_mkdir(make_path.ptr, mode) < 0 && errno != EEXIST)
280
- failed = true;
256
+ /* remove trailing slashes on path */
257
+ while (make_path.ptr[make_path.size - 1] == '/') {
258
+ make_path.size--;
259
+ make_path.ptr[make_path.size] = '\0';
260
+ }
281
261
 
282
- *sp = '/';
262
+ /* if we are not supposed to made the last element, truncate it */
263
+ if ((flags & GIT_MKDIR_SKIP_LAST) != 0)
264
+ git_buf_rtruncate_at_char(&make_path, '/');
265
+
266
+ /* if we are not supposed to make the whole path, reset root */
267
+ if ((flags & GIT_MKDIR_PATH) == 0)
268
+ root = git_buf_rfind(&make_path, '/');
269
+
270
+ /* clip root to make_path length */
271
+ if (root >= (ssize_t)make_path.size)
272
+ root = (ssize_t)make_path.size - 1;
273
+ if (root < 0)
274
+ root = 0;
275
+
276
+ tail = & make_path.ptr[root];
277
+
278
+ while (*tail) {
279
+ /* advance tail to include next path component */
280
+ while (*tail == '/')
281
+ tail++;
282
+ while (*tail && *tail != '/')
283
+ tail++;
284
+
285
+ /* truncate path at next component */
286
+ lastch = *tail;
287
+ *tail = '\0';
288
+
289
+ /* make directory */
290
+ if (p_mkdir(make_path.ptr, mode) < 0 &&
291
+ (errno != EEXIST || (flags & GIT_MKDIR_EXCL) != 0))
292
+ {
293
+ giterr_set(GITERR_OS, "Failed to make directory '%s'",
294
+ make_path.ptr);
295
+ goto fail;
283
296
  }
284
297
 
285
- pp = sp + 1;
286
- }
298
+ /* chmod if requested */
299
+ if ((flags & GIT_MKDIR_CHMOD_PATH) != 0 ||
300
+ ((flags & GIT_MKDIR_CHMOD) != 0 && lastch == '\0'))
301
+ {
302
+ if (p_chmod(make_path.ptr, mode) < 0) {
303
+ giterr_set(GITERR_OS, "Failed to set permissions on '%s'",
304
+ make_path.ptr);
305
+ goto fail;
306
+ }
307
+ }
287
308
 
288
- if (*pp != '\0' && !failed) {
289
- if (p_mkdir(make_path.ptr, mode) < 0 && errno != EEXIST)
290
- failed = true;
309
+ *tail = lastch;
291
310
  }
292
311
 
293
312
  git_buf_free(&make_path);
313
+ return 0;
294
314
 
295
- if (failed) {
296
- giterr_set(GITERR_OS,
297
- "Failed to create directory structure at '%s'", path);
298
- return -1;
299
- }
315
+ fail:
316
+ git_buf_free(&make_path);
317
+ return -1;
318
+ }
300
319
 
301
- return 0;
320
+ int git_futils_mkdir_r(const char *path, const char *base, const mode_t mode)
321
+ {
322
+ return git_futils_mkdir(path, base, mode, GIT_MKDIR_PATH);
302
323
  }
303
324
 
304
325
  static int _rmdir_recurs_foreach(void *opaque, git_buf *path)
305
326
  {
306
327
  git_directory_removal_type removal_type = *(git_directory_removal_type *)opaque;
307
328
 
308
- assert(removal_type == GIT_DIRREMOVAL_EMPTY_HIERARCHY
309
- || removal_type == GIT_DIRREMOVAL_FILES_AND_DIRS
310
- || removal_type == GIT_DIRREMOVAL_ONLY_EMPTY_DIRS);
311
-
312
329
  if (git_path_isdir(path->ptr) == true) {
313
330
  if (git_path_direach(path, _rmdir_recurs_foreach, opaque) < 0)
314
331
  return -1;
@@ -341,135 +358,89 @@ static int _rmdir_recurs_foreach(void *opaque, git_buf *path)
341
358
  return 0;
342
359
  }
343
360
 
344
- int git_futils_rmdir_r(const char *path, git_directory_removal_type removal_type)
361
+ int git_futils_rmdir_r(
362
+ const char *path, const char *base, git_directory_removal_type removal_type)
345
363
  {
346
364
  int error;
347
- git_buf p = GIT_BUF_INIT;
348
-
349
- error = git_buf_sets(&p, path);
350
- if (!error)
351
- error = _rmdir_recurs_foreach(&removal_type, &p);
352
- git_buf_free(&p);
353
- return error;
354
- }
355
-
356
- #ifdef GIT_WIN32
357
- struct win32_path {
358
- wchar_t path[MAX_PATH];
359
- DWORD len;
360
- };
361
-
362
- static int win32_expand_path(struct win32_path *s_root, const wchar_t *templ)
363
- {
364
- s_root->len = ExpandEnvironmentStringsW(templ, s_root->path, MAX_PATH);
365
- return s_root->len ? 0 : -1;
366
- }
367
-
368
- static int win32_find_file(git_buf *path, const struct win32_path *root, const char *filename)
369
- {
370
- int error = 0;
371
- size_t len;
372
- wchar_t *file_utf16 = NULL;
373
- char *file_utf8 = NULL;
374
-
375
- if (!root || !filename || (len = strlen(filename)) == 0)
376
- return GIT_ENOTFOUND;
377
-
378
- /* allocate space for wchar_t path to file */
379
- file_utf16 = git__calloc(root->len + len + 2, sizeof(wchar_t));
380
- GITERR_CHECK_ALLOC(file_utf16);
365
+ git_buf fullpath = GIT_BUF_INIT;
381
366
 
382
- /* append root + '\\' + filename as wchar_t */
383
- memcpy(file_utf16, root->path, root->len * sizeof(wchar_t));
384
-
385
- if (*filename == '/' || *filename == '\\')
386
- filename++;
367
+ assert(removal_type == GIT_DIRREMOVAL_EMPTY_HIERARCHY
368
+ || removal_type == GIT_DIRREMOVAL_FILES_AND_DIRS
369
+ || removal_type == GIT_DIRREMOVAL_ONLY_EMPTY_DIRS);
387
370
 
388
- if (gitwin_append_utf16(file_utf16 + root->len - 1, filename, len + 1) !=
389
- (int)len + 1) {
390
- error = -1;
391
- goto cleanup;
392
- }
371
+ /* build path and find "root" where we should start calling mkdir */
372
+ if (git_path_join_unrooted(&fullpath, path, base, NULL) < 0)
373
+ return -1;
393
374
 
394
- /* check access */
395
- if (_waccess(file_utf16, F_OK) < 0) {
396
- error = GIT_ENOTFOUND;
397
- goto cleanup;
398
- }
375
+ error = _rmdir_recurs_foreach(&removal_type, &fullpath);
399
376
 
400
- /* convert to utf8 */
401
- if ((file_utf8 = gitwin_from_utf16(file_utf16)) == NULL)
402
- error = -1;
403
- else {
404
- git_path_mkposix(file_utf8);
405
- git_buf_attach(path, file_utf8, 0);
406
- }
377
+ git_buf_free(&fullpath);
407
378
 
408
- cleanup:
409
- git__free(file_utf16);
410
379
  return error;
411
380
  }
412
- #endif
413
381
 
414
382
  int git_futils_find_system_file(git_buf *path, const char *filename)
415
383
  {
416
384
  #ifdef GIT_WIN32
417
- struct win32_path root;
418
-
419
- if (win32_expand_path(&root, L"%PROGRAMFILES%\\Git\\etc\\") < 0 ||
420
- root.path[0] == L'%') /* i.e. no expansion happened */
421
- {
422
- giterr_set(GITERR_OS, "Cannot locate the system's Program Files directory");
423
- return -1;
424
- }
425
-
426
- if (win32_find_file(path, &root, filename) < 0) {
427
- giterr_set(GITERR_OS, "The system file '%s' doesn't exist", filename);
428
- git_buf_clear(path);
429
- return GIT_ENOTFOUND;
430
- }
431
-
432
- return 0;
385
+ // try to find git.exe/git.cmd on path
386
+ if (!win32_find_system_file_using_path(path, filename))
387
+ return 0;
433
388
 
389
+ // try to find msysgit installation path using registry
390
+ if (!win32_find_system_file_using_registry(path, filename))
391
+ return 0;
434
392
  #else
435
393
  if (git_buf_joinpath(path, "/etc", filename) < 0)
436
394
  return -1;
437
395
 
438
396
  if (git_path_exists(path->ptr) == true)
439
397
  return 0;
398
+ #endif
440
399
 
441
400
  git_buf_clear(path);
442
401
  giterr_set(GITERR_OS, "The system file '%s' doesn't exist", filename);
443
402
  return GIT_ENOTFOUND;
444
- #endif
445
403
  }
446
404
 
447
405
  int git_futils_find_global_file(git_buf *path, const char *filename)
448
406
  {
449
407
  #ifdef GIT_WIN32
450
408
  struct win32_path root;
451
-
452
- if (win32_expand_path(&root, L"%USERPROFILE%\\") < 0 ||
453
- root.path[0] == L'%') /* i.e. no expansion happened */
454
- {
455
- giterr_set(GITERR_OS, "Cannot locate the user's profile directory");
456
- return -1;
409
+ static const wchar_t *tmpls[4] = {
410
+ L"%HOME%\\",
411
+ L"%HOMEDRIVE%%HOMEPATH%\\",
412
+ L"%USERPROFILE%\\",
413
+ NULL,
414
+ };
415
+ const wchar_t **tmpl;
416
+
417
+ for (tmpl = tmpls; *tmpl != NULL; tmpl++) {
418
+ /* try to expand environment variable, skipping if not set */
419
+ if (win32_expand_path(&root, *tmpl) != 0 || root.path[0] == L'%')
420
+ continue;
421
+
422
+ /* try to look up file under path */
423
+ if (!win32_find_file(path, &root, filename))
424
+ return 0;
425
+
426
+ /* No error if file not found under %HOME%, b/c we don't trust it,
427
+ * but do error if another var is set and yet file is not found.
428
+ */
429
+ if (tmpl != tmpls)
430
+ break;
457
431
  }
458
432
 
459
- if (win32_find_file(path, &root, filename) < 0) {
460
- giterr_set(GITERR_OS, "The global file '%s' doesn't exist", filename);
461
- git_buf_clear(path);
462
- return GIT_ENOTFOUND;
463
- }
433
+ giterr_set(GITERR_OS, "The global file '%s' doesn't exist", filename);
434
+ git_buf_clear(path);
464
435
 
465
- return 0;
436
+ return GIT_ENOTFOUND;
466
437
  #else
467
438
  const char *home = getenv("HOME");
468
439
 
469
440
  if (home == NULL) {
470
441
  giterr_set(GITERR_OS, "Global file lookup failed. "
471
442
  "Cannot locate the user's home directory");
472
- return -1;
443
+ return GIT_ENOTFOUND;
473
444
  }
474
445
 
475
446
  if (git_buf_joinpath(path, home, filename) < 0)
@@ -495,3 +466,197 @@ int git_futils_fake_symlink(const char *old, const char *new)
495
466
  }
496
467
  return retcode;
497
468
  }
469
+
470
+ static int cp_by_fd(int ifd, int ofd, bool close_fd_when_done)
471
+ {
472
+ int error = 0;
473
+ char buffer[4096];
474
+ ssize_t len = 0;
475
+
476
+ while (!error && (len = p_read(ifd, buffer, sizeof(buffer))) > 0)
477
+ /* p_write() does not have the same semantics as write(). It loops
478
+ * internally and will return 0 when it has completed writing.
479
+ */
480
+ error = p_write(ofd, buffer, len);
481
+
482
+ if (len < 0) {
483
+ giterr_set(GITERR_OS, "Read error while copying file");
484
+ error = (int)len;
485
+ }
486
+
487
+ if (close_fd_when_done) {
488
+ p_close(ifd);
489
+ p_close(ofd);
490
+ }
491
+
492
+ return error;
493
+ }
494
+
495
+ int git_futils_cp(const char *from, const char *to, mode_t filemode)
496
+ {
497
+ int ifd, ofd;
498
+
499
+ if ((ifd = git_futils_open_ro(from)) < 0)
500
+ return ifd;
501
+
502
+ if ((ofd = p_open(to, O_WRONLY | O_CREAT | O_EXCL, filemode)) < 0) {
503
+ if (errno == ENOENT || errno == ENOTDIR)
504
+ ofd = GIT_ENOTFOUND;
505
+ giterr_set(GITERR_OS, "Failed to open '%s' for writing", to);
506
+ p_close(ifd);
507
+ return ofd;
508
+ }
509
+
510
+ return cp_by_fd(ifd, ofd, true);
511
+ }
512
+
513
+ static int cp_link(const char *from, const char *to, size_t link_size)
514
+ {
515
+ int error = 0;
516
+ ssize_t read_len;
517
+ char *link_data = git__malloc(link_size + 1);
518
+ GITERR_CHECK_ALLOC(link_data);
519
+
520
+ read_len = p_readlink(from, link_data, link_size);
521
+ if (read_len != (ssize_t)link_size) {
522
+ giterr_set(GITERR_OS, "Failed to read symlink data for '%s'", from);
523
+ error = -1;
524
+ }
525
+ else {
526
+ link_data[read_len] = '\0';
527
+
528
+ if (p_symlink(link_data, to) < 0) {
529
+ giterr_set(GITERR_OS, "Could not symlink '%s' as '%s'",
530
+ link_data, to);
531
+ error = -1;
532
+ }
533
+ }
534
+
535
+ git__free(link_data);
536
+ return error;
537
+ }
538
+
539
+ typedef struct {
540
+ const char *to_root;
541
+ git_buf to;
542
+ ssize_t from_prefix;
543
+ uint32_t flags;
544
+ uint32_t mkdir_flags;
545
+ mode_t dirmode;
546
+ } cp_r_info;
547
+
548
+ static int _cp_r_callback(void *ref, git_buf *from)
549
+ {
550
+ cp_r_info *info = ref;
551
+ struct stat from_st, to_st;
552
+ bool exists = false;
553
+
554
+ if ((info->flags & GIT_CPDIR_COPY_DOTFILES) == 0 &&
555
+ from->ptr[git_path_basename_offset(from)] == '.')
556
+ return 0;
557
+
558
+ if (git_buf_joinpath(
559
+ &info->to, info->to_root, from->ptr + info->from_prefix) < 0)
560
+ return -1;
561
+
562
+ if (p_lstat(info->to.ptr, &to_st) < 0) {
563
+ if (errno != ENOENT && errno != ENOTDIR) {
564
+ giterr_set(GITERR_OS,
565
+ "Could not access %s while copying files", info->to.ptr);
566
+ return -1;
567
+ }
568
+ } else
569
+ exists = true;
570
+
571
+ if (git_path_lstat(from->ptr, &from_st) < 0)
572
+ return -1;
573
+
574
+ if (S_ISDIR(from_st.st_mode)) {
575
+ int error = 0;
576
+ mode_t oldmode = info->dirmode;
577
+
578
+ /* if we are not chmod'ing, then overwrite dirmode */
579
+ if ((info->flags & GIT_CPDIR_CHMOD) == 0)
580
+ info->dirmode = from_st.st_mode;
581
+
582
+ /* make directory now if CREATE_EMPTY_DIRS is requested and needed */
583
+ if (!exists && (info->flags & GIT_CPDIR_CREATE_EMPTY_DIRS) != 0)
584
+ error = git_futils_mkdir(
585
+ info->to.ptr, NULL, info->dirmode, info->mkdir_flags);
586
+
587
+ /* recurse onto target directory */
588
+ if (!exists || S_ISDIR(to_st.st_mode))
589
+ error = git_path_direach(from, _cp_r_callback, info);
590
+
591
+ if (oldmode != 0)
592
+ info->dirmode = oldmode;
593
+
594
+ return error;
595
+ }
596
+
597
+ if (exists) {
598
+ if ((info->flags & GIT_CPDIR_OVERWRITE) == 0)
599
+ return 0;
600
+
601
+ if (p_unlink(info->to.ptr) < 0) {
602
+ giterr_set(GITERR_OS, "Cannot overwrite existing file '%s'",
603
+ info->to.ptr);
604
+ return -1;
605
+ }
606
+ }
607
+
608
+ /* Done if this isn't a regular file or a symlink */
609
+ if (!S_ISREG(from_st.st_mode) &&
610
+ (!S_ISLNK(from_st.st_mode) ||
611
+ (info->flags & GIT_CPDIR_COPY_SYMLINKS) == 0))
612
+ return 0;
613
+
614
+ /* Make container directory on demand if needed */
615
+ if ((info->flags & GIT_CPDIR_CREATE_EMPTY_DIRS) == 0 &&
616
+ git_futils_mkdir(
617
+ info->to.ptr, NULL, info->dirmode, info->mkdir_flags) < 0)
618
+ return -1;
619
+
620
+ /* make symlink or regular file */
621
+ if (S_ISLNK(from_st.st_mode))
622
+ return cp_link(from->ptr, info->to.ptr, (size_t)from_st.st_size);
623
+ else
624
+ return git_futils_cp(from->ptr, info->to.ptr, from_st.st_mode);
625
+ }
626
+
627
+ int git_futils_cp_r(
628
+ const char *from,
629
+ const char *to,
630
+ uint32_t flags,
631
+ mode_t dirmode)
632
+ {
633
+ int error;
634
+ git_buf path = GIT_BUF_INIT;
635
+ cp_r_info info;
636
+
637
+ if (git_buf_sets(&path, from) < 0)
638
+ return -1;
639
+
640
+ info.to_root = to;
641
+ info.flags = flags;
642
+ info.dirmode = dirmode;
643
+ info.from_prefix = path.size;
644
+ git_buf_init(&info.to, 0);
645
+
646
+ /* precalculate mkdir flags */
647
+ if ((flags & GIT_CPDIR_CREATE_EMPTY_DIRS) == 0) {
648
+ info.mkdir_flags = GIT_MKDIR_PATH | GIT_MKDIR_SKIP_LAST;
649
+ if ((flags & GIT_CPDIR_CHMOD) != 0)
650
+ info.mkdir_flags |= GIT_MKDIR_CHMOD_PATH;
651
+ } else {
652
+ info.mkdir_flags =
653
+ ((flags & GIT_CPDIR_CHMOD) != 0) ? GIT_MKDIR_CHMOD : 0;
654
+ }
655
+
656
+ error = _cp_r_callback(&info, &path);
657
+
658
+ git_buf_free(&path);
659
+ git_buf_free(&info.to);
660
+
661
+ return error;
662
+ }