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
@@ -13,6 +13,8 @@
13
13
  #include "transport.h"
14
14
  #include "repository.h"
15
15
 
16
+ #define GIT_REMOTE_ORIGIN "origin"
17
+
16
18
  struct git_remote {
17
19
  char *name;
18
20
  char *url;
@@ -24,7 +26,8 @@ struct git_remote {
24
26
  git_repository *repo;
25
27
  git_remote_callbacks callbacks;
26
28
  unsigned int need_pack:1,
27
- check_cert;
29
+ download_tags:2, /* There are four possible values */
30
+ check_cert:1;
28
31
  };
29
32
 
30
33
  const char* git_remote__urlfordirection(struct git_remote *remote, int direction);
@@ -0,0 +1,58 @@
1
+ /*
2
+ * Copyright (C) 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_repo_template_h__
8
+ #define INCLUDE_repo_template_h__
9
+
10
+ #define GIT_OBJECTS_INFO_DIR GIT_OBJECTS_DIR "info/"
11
+ #define GIT_OBJECTS_PACK_DIR GIT_OBJECTS_DIR "pack/"
12
+
13
+ #define GIT_HOOKS_DIR "hooks/"
14
+ #define GIT_HOOKS_DIR_MODE 0755
15
+
16
+ #define GIT_HOOKS_README_FILE GIT_HOOKS_DIR "README.sample"
17
+ #define GIT_HOOKS_README_MODE 0755
18
+ #define GIT_HOOKS_README_CONTENT \
19
+ "#!/bin/sh\n"\
20
+ "#\n"\
21
+ "# Place appropriately named executable hook scripts into this directory\n"\
22
+ "# to intercept various actions that git takes. See `git help hooks` for\n"\
23
+ "# more information.\n"
24
+
25
+ #define GIT_INFO_DIR "info/"
26
+ #define GIT_INFO_DIR_MODE 0755
27
+
28
+ #define GIT_INFO_EXCLUDE_FILE GIT_INFO_DIR "exclude"
29
+ #define GIT_INFO_EXCLUDE_MODE 0644
30
+ #define GIT_INFO_EXCLUDE_CONTENT \
31
+ "# File patterns to ignore; see `git help ignore` for more information.\n"\
32
+ "# Lines that start with '#' are comments.\n"
33
+
34
+ #define GIT_DESC_FILE "description"
35
+ #define GIT_DESC_MODE 0644
36
+ #define GIT_DESC_CONTENT \
37
+ "Unnamed repository; edit this file 'description' to name the repository.\n"
38
+
39
+ typedef struct {
40
+ const char *path;
41
+ mode_t mode;
42
+ const char *content;
43
+ } repo_template_item;
44
+
45
+ static repo_template_item repo_template[] = {
46
+ { GIT_OBJECTS_INFO_DIR, GIT_OBJECT_DIR_MODE, NULL }, /* '/objects/info/' */
47
+ { GIT_OBJECTS_PACK_DIR, GIT_OBJECT_DIR_MODE, NULL }, /* '/objects/pack/' */
48
+ { GIT_REFS_HEADS_DIR, GIT_REFS_DIR_MODE, NULL }, /* '/refs/heads/' */
49
+ { GIT_REFS_TAGS_DIR, GIT_REFS_DIR_MODE, NULL }, /* '/refs/tags/' */
50
+ { GIT_HOOKS_DIR, GIT_HOOKS_DIR_MODE, NULL }, /* '/hooks/' */
51
+ { GIT_INFO_DIR, GIT_INFO_DIR_MODE, NULL }, /* '/info/' */
52
+ { GIT_DESC_FILE, GIT_DESC_MODE, GIT_DESC_CONTENT },
53
+ { GIT_HOOKS_README_FILE, GIT_HOOKS_README_MODE, GIT_HOOKS_README_CONTENT },
54
+ { GIT_INFO_EXCLUDE_FILE, GIT_INFO_EXCLUDE_MODE, GIT_INFO_EXCLUDE_CONTENT },
55
+ { NULL, 0, NULL }
56
+ };
57
+
58
+ #endif
@@ -17,9 +17,9 @@
17
17
  #include "fileops.h"
18
18
  #include "config.h"
19
19
  #include "refs.h"
20
-
21
- #define GIT_OBJECTS_INFO_DIR GIT_OBJECTS_DIR "info/"
22
- #define GIT_OBJECTS_PACK_DIR GIT_OBJECTS_DIR "pack/"
20
+ #include "filter.h"
21
+ #include "odb.h"
22
+ #include "remote.h"
23
23
 
24
24
  #define GIT_FILE_CONTENT_PREFIX "gitdir:"
25
25
 
@@ -27,6 +27,8 @@
27
27
 
28
28
  #define GIT_REPO_VERSION 0
29
29
 
30
+ #define GIT_TEMPLATE_DIR "/usr/share/git-core/templates"
31
+
30
32
  static void drop_odb(git_repository *repo)
31
33
  {
32
34
  if (repo->_odb != NULL) {
@@ -147,8 +149,13 @@ static int load_workdir(git_repository *repo, git_buf *parent_path)
147
149
  return -1;
148
150
 
149
151
  error = git_config_get_string(&worktree, config, "core.worktree");
150
- if (!error && worktree != NULL)
151
- repo->workdir = git__strdup(worktree);
152
+ if (!error && worktree != NULL) {
153
+ error = git_path_prettify_dir(
154
+ &worktree_buf, worktree, repo->path_repository);
155
+ if (error < 0)
156
+ return error;
157
+ repo->workdir = git_buf_detach(&worktree_buf);
158
+ }
152
159
  else if (error != GIT_ENOTFOUND)
153
160
  return error;
154
161
  else {
@@ -238,16 +245,17 @@ static int read_gitfile(git_buf *path_out, const char *file_path)
238
245
 
239
246
  git_buf_rtrim(&file);
240
247
 
241
- if (file.size <= prefix_len ||
242
- memcmp(file.ptr, GIT_FILE_CONTENT_PREFIX, prefix_len) != 0)
248
+ if (git_buf_len(&file) <= prefix_len ||
249
+ memcmp(git_buf_cstr(&file), GIT_FILE_CONTENT_PREFIX, prefix_len) != 0)
243
250
  {
244
251
  giterr_set(GITERR_REPOSITORY, "The `.git` file at '%s' is malformed", file_path);
245
252
  error = -1;
246
253
  }
247
254
  else if ((error = git_path_dirname_r(path_out, file_path)) >= 0) {
248
- const char *gitlink = ((const char *)file.ptr) + prefix_len;
255
+ const char *gitlink = git_buf_cstr(&file) + prefix_len;
249
256
  while (*gitlink && git__isspace(*gitlink)) gitlink++;
250
- error = git_path_prettify_dir(path_out, gitlink, path_out->ptr);
257
+ error = git_path_prettify_dir(
258
+ path_out, gitlink, git_buf_cstr(path_out));
251
259
  }
252
260
 
253
261
  git_buf_free(&file);
@@ -359,9 +367,11 @@ int git_repository_open_ext(
359
367
  git_buf path = GIT_BUF_INIT, parent = GIT_BUF_INIT;
360
368
  git_repository *repo;
361
369
 
362
- *repo_ptr = NULL;
370
+ if (repo_ptr)
371
+ *repo_ptr = NULL;
363
372
 
364
- if ((error = find_repo(&path, &parent, start_path, flags, ceiling_dirs)) < 0)
373
+ error = find_repo(&path, &parent, start_path, flags, ceiling_dirs);
374
+ if (error < 0 || !repo_ptr)
365
375
  return error;
366
376
 
367
377
  repo = repository_alloc();
@@ -421,7 +431,7 @@ int git_repository_discover(
421
431
 
422
432
  if (size < (size_t)(path.size + 1)) {
423
433
  giterr_set(GITERR_REPOSITORY,
424
- "The given buffer is too long to store the discovered path");
434
+ "The given buffer is too small to store the discovered path");
425
435
  git_buf_free(&path);
426
436
  return -1;
427
437
  }
@@ -436,6 +446,7 @@ static int load_config(
436
446
  git_config **out,
437
447
  git_repository *repo,
438
448
  const char *global_config_path,
449
+ const char *xdg_config_path,
439
450
  const char *system_config_path)
440
451
  {
441
452
  git_buf config_path = GIT_BUF_INIT;
@@ -450,18 +461,23 @@ static int load_config(
450
461
  &config_path, repo->path_repository, GIT_CONFIG_FILENAME_INREPO) < 0)
451
462
  goto on_error;
452
463
 
453
- if (git_config_add_file_ondisk(cfg, config_path.ptr, 3) < 0)
464
+ if (git_config_add_file_ondisk(cfg, config_path.ptr, GIT_CONFIG_LEVEL_LOCAL, 0) < 0)
454
465
  goto on_error;
455
466
 
456
467
  git_buf_free(&config_path);
457
468
 
458
469
  if (global_config_path != NULL) {
459
- if (git_config_add_file_ondisk(cfg, global_config_path, 2) < 0)
470
+ if (git_config_add_file_ondisk(cfg, global_config_path, GIT_CONFIG_LEVEL_GLOBAL, 0) < 0)
471
+ goto on_error;
472
+ }
473
+
474
+ if (xdg_config_path != NULL) {
475
+ if (git_config_add_file_ondisk(cfg, xdg_config_path, GIT_CONFIG_LEVEL_XDG, 0) < 0)
460
476
  goto on_error;
461
477
  }
462
478
 
463
479
  if (system_config_path != NULL) {
464
- if (git_config_add_file_ondisk(cfg, system_config_path, 1) < 0)
480
+ if (git_config_add_file_ondisk(cfg, system_config_path, GIT_CONFIG_LEVEL_SYSTEM, 0) < 0)
465
481
  goto on_error;
466
482
  }
467
483
 
@@ -478,21 +494,26 @@ on_error:
478
494
  int git_repository_config__weakptr(git_config **out, git_repository *repo)
479
495
  {
480
496
  if (repo->_config == NULL) {
481
- git_buf global_buf = GIT_BUF_INIT, system_buf = GIT_BUF_INIT;
497
+ git_buf global_buf = GIT_BUF_INIT, xdg_buf = GIT_BUF_INIT, system_buf = GIT_BUF_INIT;
482
498
  int res;
483
499
 
484
500
  const char *global_config_path = NULL;
501
+ const char *xdg_config_path = NULL;
485
502
  const char *system_config_path = NULL;
486
503
 
487
504
  if (git_config_find_global_r(&global_buf) == 0)
488
505
  global_config_path = global_buf.ptr;
489
506
 
507
+ if (git_config_find_xdg_r(&xdg_buf) == 0)
508
+ xdg_config_path = xdg_buf.ptr;
509
+
490
510
  if (git_config_find_system_r(&system_buf) == 0)
491
511
  system_config_path = system_buf.ptr;
492
512
 
493
- res = load_config(&repo->_config, repo, global_config_path, system_config_path);
513
+ res = load_config(&repo->_config, repo, global_config_path, xdg_config_path, system_config_path);
494
514
 
495
515
  git_buf_free(&global_buf);
516
+ git_buf_free(&xdg_buf);
496
517
  git_buf_free(&system_buf);
497
518
 
498
519
  if (res < 0)
@@ -632,19 +653,35 @@ static int check_repositoryformatversion(git_config *config)
632
653
  return 0;
633
654
  }
634
655
 
635
- static int repo_init_createhead(const char *git_dir)
656
+ static int repo_init_create_head(const char *git_dir, const char *ref_name)
636
657
  {
637
658
  git_buf ref_path = GIT_BUF_INIT;
638
659
  git_filebuf ref = GIT_FILEBUF_INIT;
660
+ const char *fmt;
639
661
 
640
662
  if (git_buf_joinpath(&ref_path, git_dir, GIT_HEAD_FILE) < 0 ||
641
- git_filebuf_open(&ref, ref_path.ptr, 0) < 0 ||
642
- git_filebuf_printf(&ref, "ref: refs/heads/master\n") < 0 ||
663
+ git_filebuf_open(&ref, ref_path.ptr, 0) < 0)
664
+ goto fail;
665
+
666
+ if (!ref_name)
667
+ ref_name = GIT_BRANCH_MASTER;
668
+
669
+ if (git__prefixcmp(ref_name, GIT_REFS_DIR) == 0)
670
+ fmt = "ref: %s\n";
671
+ else
672
+ fmt = "ref: " GIT_REFS_HEADS_DIR "%s\n";
673
+
674
+ if (git_filebuf_printf(&ref, fmt, ref_name) < 0 ||
643
675
  git_filebuf_commit(&ref, GIT_REFS_FILE_MODE) < 0)
644
- return -1;
676
+ goto fail;
645
677
 
646
678
  git_buf_free(&ref_path);
647
679
  return 0;
680
+
681
+ fail:
682
+ git_buf_free(&ref_path);
683
+ git_filebuf_cleanup(&ref);
684
+ return -1;
648
685
  }
649
686
 
650
687
  static bool is_chmod_supported(const char *file_path)
@@ -665,6 +702,7 @@ static bool is_chmod_supported(const char *file_path)
665
702
  return false;
666
703
 
667
704
  _is_supported = (st1.st_mode != st2.st_mode);
705
+
668
706
  return _is_supported;
669
707
  }
670
708
 
@@ -686,19 +724,45 @@ cleanup:
686
724
  return _is_insensitive;
687
725
  }
688
726
 
689
- static int repo_init_config(const char *git_dir, bool is_bare, bool is_reinit)
727
+ static bool are_symlinks_supported(const char *wd_path)
728
+ {
729
+ git_buf path = GIT_BUF_INIT;
730
+ int fd;
731
+ struct stat st;
732
+ static int _symlinks_supported = -1;
733
+
734
+ if (_symlinks_supported > -1)
735
+ return _symlinks_supported;
736
+
737
+ if ((fd = git_futils_mktmp(&path, wd_path)) < 0 ||
738
+ p_close(fd) < 0 ||
739
+ p_unlink(path.ptr) < 0 ||
740
+ p_symlink("testing", path.ptr) < 0 ||
741
+ p_lstat(path.ptr, &st) < 0)
742
+ _symlinks_supported = false;
743
+ else
744
+ _symlinks_supported = (S_ISLNK(st.st_mode) != 0);
745
+
746
+ (void)p_unlink(path.ptr);
747
+ git_buf_free(&path);
748
+
749
+ return _symlinks_supported;
750
+ }
751
+
752
+ static int repo_init_config(
753
+ const char *repo_dir,
754
+ const char *work_dir,
755
+ git_repository_init_options *opts)
690
756
  {
757
+ int error = 0;
691
758
  git_buf cfg_path = GIT_BUF_INIT;
692
759
  git_config *config = NULL;
693
760
 
694
- #define SET_REPO_CONFIG(type, name, val) {\
695
- if (git_config_set_##type(config, name, val) < 0) { \
696
- git_buf_free(&cfg_path); \
697
- git_config_free(config); \
698
- return -1; } \
699
- }
761
+ #define SET_REPO_CONFIG(TYPE, NAME, VAL) do {\
762
+ if ((error = git_config_set_##TYPE(config, NAME, VAL)) < 0) \
763
+ goto cleanup; } while (0)
700
764
 
701
- if (git_buf_joinpath(&cfg_path, git_dir, GIT_CONFIG_FILENAME_INREPO) < 0)
765
+ if (git_buf_joinpath(&cfg_path, repo_dir, GIT_CONFIG_FILENAME_INREPO) < 0)
702
766
  return -1;
703
767
 
704
768
  if (git_config_open_ondisk(&config, git_buf_cstr(&cfg_path)) < 0) {
@@ -706,58 +770,61 @@ static int repo_init_config(const char *git_dir, bool is_bare, bool is_reinit)
706
770
  return -1;
707
771
  }
708
772
 
709
- if (is_reinit && check_repositoryformatversion(config) < 0) {
710
- git_buf_free(&cfg_path);
711
- git_config_free(config);
712
- return -1;
713
- }
773
+ if ((opts->flags & GIT_REPOSITORY_INIT__IS_REINIT) != 0 &&
774
+ (error = check_repositoryformatversion(config)) < 0)
775
+ goto cleanup;
714
776
 
715
- SET_REPO_CONFIG(bool, "core.bare", is_bare);
716
- SET_REPO_CONFIG(int32, "core.repositoryformatversion", GIT_REPO_VERSION);
717
- SET_REPO_CONFIG(bool, "core.filemode", is_chmod_supported(git_buf_cstr(&cfg_path)));
718
-
719
- if (!is_bare)
720
- SET_REPO_CONFIG(bool, "core.logallrefupdates", true);
777
+ SET_REPO_CONFIG(
778
+ bool, "core.bare", (opts->flags & GIT_REPOSITORY_INIT_BARE) != 0);
779
+ SET_REPO_CONFIG(
780
+ int32, "core.repositoryformatversion", GIT_REPO_VERSION);
781
+ SET_REPO_CONFIG(
782
+ bool, "core.filemode", is_chmod_supported(git_buf_cstr(&cfg_path)));
721
783
 
722
- if (!is_reinit && is_filesystem_case_insensitive(git_dir))
723
- SET_REPO_CONFIG(bool, "core.ignorecase", true);
724
- /* TODO: what other defaults? */
784
+ if (!(opts->flags & GIT_REPOSITORY_INIT_BARE)) {
785
+ SET_REPO_CONFIG(bool, "core.logallrefupdates", true);
725
786
 
726
- git_buf_free(&cfg_path);
727
- git_config_free(config);
728
- return 0;
729
- }
787
+ if (!are_symlinks_supported(work_dir))
788
+ SET_REPO_CONFIG(bool, "core.symlinks", false);
730
789
 
731
- #define GIT_HOOKS_DIR "hooks/"
732
- #define GIT_HOOKS_DIR_MODE 0755
790
+ if (!(opts->flags & GIT_REPOSITORY_INIT__NATURAL_WD)) {
791
+ SET_REPO_CONFIG(string, "core.worktree", work_dir);
792
+ }
793
+ else if ((opts->flags & GIT_REPOSITORY_INIT__IS_REINIT) != 0) {
794
+ if (git_config_delete(config, "core.worktree") < 0)
795
+ giterr_clear();
796
+ }
797
+ } else {
798
+ if (!are_symlinks_supported(repo_dir))
799
+ SET_REPO_CONFIG(bool, "core.symlinks", false);
800
+ }
733
801
 
734
- #define GIT_HOOKS_README_FILE GIT_HOOKS_DIR "README.sample"
735
- #define GIT_HOOKS_README_MODE 0755
736
- #define GIT_HOOKS_README_CONTENT \
737
- "#!/bin/sh\n"\
738
- "#\n"\
739
- "# Place appropriately named executable hook scripts into this directory\n"\
740
- "# to intercept various actions that git takes. See `git help hooks` for\n"\
741
- "# more information.\n"
802
+ if (!(opts->flags & GIT_REPOSITORY_INIT__IS_REINIT) &&
803
+ is_filesystem_case_insensitive(repo_dir))
804
+ SET_REPO_CONFIG(bool, "core.ignorecase", true);
742
805
 
743
- #define GIT_INFO_DIR "info/"
744
- #define GIT_INFO_DIR_MODE 0755
806
+ if (opts->mode == GIT_REPOSITORY_INIT_SHARED_GROUP) {
807
+ SET_REPO_CONFIG(int32, "core.sharedrepository", 1);
808
+ SET_REPO_CONFIG(bool, "receive.denyNonFastforwards", true);
809
+ }
810
+ else if (opts->mode == GIT_REPOSITORY_INIT_SHARED_ALL) {
811
+ SET_REPO_CONFIG(int32, "core.sharedrepository", 2);
812
+ SET_REPO_CONFIG(bool, "receive.denyNonFastforwards", true);
813
+ }
745
814
 
746
- #define GIT_INFO_EXCLUDE_FILE GIT_INFO_DIR "exclude"
747
- #define GIT_INFO_EXCLUDE_MODE 0644
748
- #define GIT_INFO_EXCLUDE_CONTENT \
749
- "# File patterns to ignore; see `git help ignore` for more information.\n"\
750
- "# Lines that start with '#' are comments.\n"
815
+ cleanup:
816
+ git_buf_free(&cfg_path);
817
+ git_config_free(config);
751
818
 
752
- #define GIT_DESC_FILE "description"
753
- #define GIT_DESC_MODE 0644
754
- #define GIT_DESC_CONTENT "Unnamed repository; edit this file 'description' to name the repository.\n"
819
+ return error;
820
+ }
755
821
 
756
822
  static int repo_write_template(
757
823
  const char *git_dir,
758
824
  bool allow_overwrite,
759
825
  const char *file,
760
826
  mode_t mode,
827
+ bool hidden,
761
828
  const char *content)
762
829
  {
763
830
  git_buf path = GIT_BUF_INIT;
@@ -781,6 +848,15 @@ static int repo_write_template(
781
848
  else if (errno != EEXIST)
782
849
  error = fd;
783
850
 
851
+ #ifdef GIT_WIN32
852
+ if (!error && hidden) {
853
+ if (p_hide_directory__w32(path.ptr) < 0)
854
+ error = -1;
855
+ }
856
+ #else
857
+ GIT_UNUSED(hidden);
858
+ #endif
859
+
784
860
  git_buf_free(&path);
785
861
 
786
862
  if (error)
@@ -790,86 +866,319 @@ static int repo_write_template(
790
866
  return error;
791
867
  }
792
868
 
793
- static int repo_init_structure(const char *git_dir, int is_bare)
869
+ static int repo_write_gitlink(
870
+ const char *in_dir, const char *to_repo)
794
871
  {
795
- int i;
796
- struct { const char *dir; mode_t mode; } dirs[] = {
797
- { GIT_OBJECTS_INFO_DIR, GIT_OBJECT_DIR_MODE }, /* '/objects/info/' */
798
- { GIT_OBJECTS_PACK_DIR, GIT_OBJECT_DIR_MODE }, /* '/objects/pack/' */
799
- { GIT_REFS_HEADS_DIR, GIT_REFS_DIR_MODE }, /* '/refs/heads/' */
800
- { GIT_REFS_TAGS_DIR, GIT_REFS_DIR_MODE }, /* '/refs/tags/' */
801
- { GIT_HOOKS_DIR, GIT_HOOKS_DIR_MODE }, /* '/hooks/' */
802
- { GIT_INFO_DIR, GIT_INFO_DIR_MODE }, /* '/info/' */
803
- { NULL, 0 }
804
- };
805
- struct { const char *file; mode_t mode; const char *content; } tmpl[] = {
806
- { GIT_DESC_FILE, GIT_DESC_MODE, GIT_DESC_CONTENT },
807
- { GIT_HOOKS_README_FILE, GIT_HOOKS_README_MODE, GIT_HOOKS_README_CONTENT },
808
- { GIT_INFO_EXCLUDE_FILE, GIT_INFO_EXCLUDE_MODE, GIT_INFO_EXCLUDE_CONTENT },
809
- { NULL, 0, NULL }
810
- };
811
-
812
- /* Make the base directory */
813
- if (git_futils_mkdir_r(git_dir, NULL, is_bare ? GIT_BARE_DIR_MODE : GIT_DIR_MODE) < 0)
872
+ int error;
873
+ git_buf buf = GIT_BUF_INIT;
874
+ struct stat st;
875
+
876
+ git_path_dirname_r(&buf, to_repo);
877
+ git_path_to_dir(&buf);
878
+ if (git_buf_oom(&buf))
814
879
  return -1;
815
880
 
816
- /* Hides the ".git" directory */
817
- if (!is_bare) {
881
+ /* don't write gitlink to natural workdir */
882
+ if (git__suffixcmp(to_repo, "/" DOT_GIT "/") == 0 &&
883
+ strcmp(in_dir, buf.ptr) == 0)
884
+ {
885
+ error = GIT_PASSTHROUGH;
886
+ goto cleanup;
887
+ }
888
+
889
+ if ((error = git_buf_joinpath(&buf, in_dir, DOT_GIT)) < 0)
890
+ goto cleanup;
891
+
892
+ if (!p_stat(buf.ptr, &st) && !S_ISREG(st.st_mode)) {
893
+ giterr_set(GITERR_REPOSITORY,
894
+ "Cannot overwrite gitlink file into path '%s'", in_dir);
895
+ error = GIT_EEXISTS;
896
+ goto cleanup;
897
+ }
898
+
899
+ git_buf_clear(&buf);
900
+
901
+ error = git_buf_printf(&buf, "%s %s", GIT_FILE_CONTENT_PREFIX, to_repo);
902
+
903
+ if (!error)
904
+ error = repo_write_template(in_dir, true, DOT_GIT, 0644, true, buf.ptr);
905
+
906
+ cleanup:
907
+ git_buf_free(&buf);
908
+ return error;
909
+ }
910
+
911
+ static mode_t pick_dir_mode(git_repository_init_options *opts)
912
+ {
913
+ if (opts->mode == GIT_REPOSITORY_INIT_SHARED_UMASK)
914
+ return 0755;
915
+ if (opts->mode == GIT_REPOSITORY_INIT_SHARED_GROUP)
916
+ return (0775 | S_ISGID);
917
+ if (opts->mode == GIT_REPOSITORY_INIT_SHARED_ALL)
918
+ return (0777 | S_ISGID);
919
+ return opts->mode;
920
+ }
921
+
922
+ #include "repo_template.h"
923
+
924
+ static int repo_init_structure(
925
+ const char *repo_dir,
926
+ const char *work_dir,
927
+ git_repository_init_options *opts)
928
+ {
929
+ int error = 0;
930
+ repo_template_item *tpl;
931
+ bool external_tpl =
932
+ ((opts->flags & GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE) != 0);
933
+ mode_t dmode = pick_dir_mode(opts);
934
+
935
+ /* Hide the ".git" directory */
818
936
  #ifdef GIT_WIN32
819
- if (p_hide_directory__w32(git_dir) < 0) {
937
+ if ((opts->flags & GIT_REPOSITORY_INIT__HAS_DOTGIT) != 0) {
938
+ if (p_hide_directory__w32(repo_dir) < 0) {
820
939
  giterr_set(GITERR_REPOSITORY,
821
940
  "Failed to mark Git repository folder as hidden");
822
941
  return -1;
823
942
  }
824
- #endif
825
943
  }
944
+ #endif
826
945
 
827
- /* Make subdirectories as needed */
828
- for (i = 0; dirs[i].dir != NULL; ++i) {
829
- if (git_futils_mkdir_r(dirs[i].dir, git_dir, dirs[i].mode) < 0)
946
+ /* Create the .git gitlink if appropriate */
947
+ if ((opts->flags & GIT_REPOSITORY_INIT_BARE) == 0 &&
948
+ (opts->flags & GIT_REPOSITORY_INIT__NATURAL_WD) == 0)
949
+ {
950
+ if (repo_write_gitlink(work_dir, repo_dir) < 0)
830
951
  return -1;
831
952
  }
832
953
 
833
- /* Make template files as needed */
834
- for (i = 0; tmpl[i].file != NULL; ++i) {
835
- if (repo_write_template(
836
- git_dir, false, tmpl[i].file, tmpl[i].mode, tmpl[i].content) < 0)
954
+ /* Copy external template if requested */
955
+ if (external_tpl) {
956
+ git_config *cfg;
957
+ const char *tdir;
958
+
959
+ if (opts->template_path)
960
+ tdir = opts->template_path;
961
+ else if ((error = git_config_open_default(&cfg)) < 0)
962
+ return error;
963
+ else {
964
+ error = git_config_get_string(&tdir, cfg, "init.templatedir");
965
+
966
+ git_config_free(cfg);
967
+
968
+ if (error && error != GIT_ENOTFOUND)
969
+ return error;
970
+
971
+ giterr_clear();
972
+ tdir = GIT_TEMPLATE_DIR;
973
+ }
974
+
975
+ error = git_futils_cp_r(tdir, repo_dir,
976
+ GIT_CPDIR_COPY_SYMLINKS | GIT_CPDIR_CHMOD, dmode);
977
+
978
+ if (error < 0) {
979
+ if (strcmp(tdir, GIT_TEMPLATE_DIR) != 0)
980
+ return error;
981
+
982
+ /* if template was default, ignore error and use internal */
983
+ giterr_clear();
984
+ external_tpl = false;
985
+ }
986
+ }
987
+
988
+ /* Copy internal template
989
+ * - always ensure existence of dirs
990
+ * - only create files if no external template was specified
991
+ */
992
+ for (tpl = repo_template; !error && tpl->path; ++tpl) {
993
+ if (!tpl->content)
994
+ error = git_futils_mkdir(
995
+ tpl->path, repo_dir, dmode, GIT_MKDIR_PATH | GIT_MKDIR_CHMOD);
996
+ else if (!external_tpl) {
997
+ const char *content = tpl->content;
998
+
999
+ if (opts->description && strcmp(tpl->path, GIT_DESC_FILE) == 0)
1000
+ content = opts->description;
1001
+
1002
+ error = repo_write_template(
1003
+ repo_dir, false, tpl->path, tpl->mode, false, content);
1004
+ }
1005
+ }
1006
+
1007
+ return error;
1008
+ }
1009
+
1010
+ static int repo_init_directories(
1011
+ git_buf *repo_path,
1012
+ git_buf *wd_path,
1013
+ const char *given_repo,
1014
+ git_repository_init_options *opts)
1015
+ {
1016
+ int error = 0;
1017
+ bool add_dotgit, has_dotgit, natural_wd;
1018
+ mode_t dirmode;
1019
+
1020
+ /* set up repo path */
1021
+
1022
+ add_dotgit =
1023
+ (opts->flags & GIT_REPOSITORY_INIT_NO_DOTGIT_DIR) == 0 &&
1024
+ (opts->flags & GIT_REPOSITORY_INIT_BARE) == 0 &&
1025
+ git__suffixcmp(given_repo, "/" DOT_GIT) != 0 &&
1026
+ git__suffixcmp(given_repo, "/" GIT_DIR) != 0;
1027
+
1028
+ if (git_buf_joinpath(repo_path, given_repo, add_dotgit ? GIT_DIR : "") < 0)
1029
+ return -1;
1030
+
1031
+ has_dotgit = (git__suffixcmp(repo_path->ptr, "/" GIT_DIR) == 0);
1032
+ if (has_dotgit)
1033
+ opts->flags |= GIT_REPOSITORY_INIT__HAS_DOTGIT;
1034
+
1035
+ /* set up workdir path */
1036
+
1037
+ if ((opts->flags & GIT_REPOSITORY_INIT_BARE) == 0) {
1038
+ if (opts->workdir_path) {
1039
+ if (git_path_join_unrooted(
1040
+ wd_path, opts->workdir_path, repo_path->ptr, NULL) < 0)
1041
+ return -1;
1042
+ } else if (has_dotgit) {
1043
+ if (git_path_dirname_r(wd_path, repo_path->ptr) < 0)
1044
+ return -1;
1045
+ } else {
1046
+ giterr_set(GITERR_REPOSITORY, "Cannot pick working directory"
1047
+ " for non-bare repository that isn't a '.git' directory");
837
1048
  return -1;
1049
+ }
1050
+
1051
+ if (git_path_to_dir(wd_path) < 0)
1052
+ return -1;
1053
+ } else {
1054
+ git_buf_clear(wd_path);
838
1055
  }
839
1056
 
840
- return 0;
1057
+ natural_wd =
1058
+ has_dotgit &&
1059
+ wd_path->size > 0 &&
1060
+ wd_path->size + strlen(GIT_DIR) == repo_path->size &&
1061
+ memcmp(repo_path->ptr, wd_path->ptr, wd_path->size) == 0;
1062
+ if (natural_wd)
1063
+ opts->flags |= GIT_REPOSITORY_INIT__NATURAL_WD;
1064
+
1065
+ /* create directories as needed / requested */
1066
+
1067
+ dirmode = pick_dir_mode(opts);
1068
+
1069
+ if ((opts->flags & GIT_REPOSITORY_INIT_MKDIR) != 0 && has_dotgit) {
1070
+ git_buf p = GIT_BUF_INIT;
1071
+ if ((error = git_path_dirname_r(&p, repo_path->ptr)) >= 0)
1072
+ error = git_futils_mkdir(p.ptr, NULL, dirmode, 0);
1073
+ git_buf_free(&p);
1074
+ }
1075
+
1076
+ if ((opts->flags & GIT_REPOSITORY_INIT_MKDIR) != 0 ||
1077
+ (opts->flags & GIT_REPOSITORY_INIT_MKPATH) != 0 ||
1078
+ has_dotgit)
1079
+ {
1080
+ uint32_t mkflag = GIT_MKDIR_CHMOD;
1081
+ if ((opts->flags & GIT_REPOSITORY_INIT_MKPATH) != 0)
1082
+ mkflag |= GIT_MKDIR_PATH;
1083
+ error = git_futils_mkdir(repo_path->ptr, NULL, dirmode, mkflag);
1084
+ }
1085
+
1086
+ if (wd_path->size > 0 &&
1087
+ !natural_wd &&
1088
+ ((opts->flags & GIT_REPOSITORY_INIT_MKDIR) != 0 ||
1089
+ (opts->flags & GIT_REPOSITORY_INIT_MKPATH) != 0))
1090
+ error = git_futils_mkdir(wd_path->ptr, NULL, dirmode & ~S_ISGID,
1091
+ (opts->flags & GIT_REPOSITORY_INIT_MKPATH) ? GIT_MKDIR_PATH : 0);
1092
+
1093
+ /* prettify both directories now that they are created */
1094
+
1095
+ if (!error) {
1096
+ error = git_path_prettify_dir(repo_path, repo_path->ptr, NULL);
1097
+
1098
+ if (!error && wd_path->size > 0)
1099
+ error = git_path_prettify_dir(wd_path, wd_path->ptr, NULL);
1100
+ }
1101
+
1102
+ return error;
841
1103
  }
842
1104
 
843
- int git_repository_init(git_repository **repo_out, const char *path, unsigned is_bare)
1105
+ static int repo_init_create_origin(git_repository *repo, const char *url)
844
1106
  {
845
- git_buf repository_path = GIT_BUF_INIT;
846
- bool is_reinit;
847
- int result = -1;
1107
+ int error;
1108
+ git_remote *remote;
848
1109
 
849
- assert(repo_out && path);
1110
+ if (!(error = git_remote_add(&remote, repo, GIT_REMOTE_ORIGIN, url))) {
1111
+ error = git_remote_save(remote);
1112
+ git_remote_free(remote);
1113
+ }
850
1114
 
851
- if (git_buf_joinpath(&repository_path, path, is_bare ? "" : GIT_DIR) < 0)
852
- goto cleanup;
1115
+ return error;
1116
+ }
853
1117
 
854
- is_reinit = git_path_isdir(repository_path.ptr) && valid_repository_path(&repository_path);
1118
+ int git_repository_init(
1119
+ git_repository **repo_out, const char *path, unsigned is_bare)
1120
+ {
1121
+ git_repository_init_options opts;
855
1122
 
856
- if (is_reinit) {
857
- /* TODO: reinitialize the templates */
1123
+ memset(&opts, 0, sizeof(opts));
1124
+ opts.flags = GIT_REPOSITORY_INIT_MKPATH; /* don't love this default */
1125
+ if (is_bare)
1126
+ opts.flags |= GIT_REPOSITORY_INIT_BARE;
858
1127
 
859
- if (repo_init_config(repository_path.ptr, is_bare, is_reinit) < 0)
860
- goto cleanup;
1128
+ return git_repository_init_ext(repo_out, path, &opts);
1129
+ }
1130
+
1131
+ int git_repository_init_ext(
1132
+ git_repository **repo_out,
1133
+ const char *given_repo,
1134
+ git_repository_init_options *opts)
1135
+ {
1136
+ int error;
1137
+ git_buf repo_path = GIT_BUF_INIT, wd_path = GIT_BUF_INIT;
1138
+
1139
+ assert(repo_out && given_repo && opts);
861
1140
 
862
- } else if (repo_init_structure(repository_path.ptr, is_bare) < 0 ||
863
- repo_init_config(repository_path.ptr, is_bare, is_reinit) < 0 ||
864
- repo_init_createhead(repository_path.ptr) < 0) {
1141
+ error = repo_init_directories(&repo_path, &wd_path, given_repo, opts);
1142
+ if (error < 0)
865
1143
  goto cleanup;
1144
+
1145
+ if (valid_repository_path(&repo_path)) {
1146
+
1147
+ if ((opts->flags & GIT_REPOSITORY_INIT_NO_REINIT) != 0) {
1148
+ giterr_set(GITERR_REPOSITORY,
1149
+ "Attempt to reinitialize '%s'", given_repo);
1150
+ error = GIT_EEXISTS;
1151
+ goto cleanup;
1152
+ }
1153
+
1154
+ opts->flags |= GIT_REPOSITORY_INIT__IS_REINIT;
1155
+
1156
+ error = repo_init_config(
1157
+ git_buf_cstr(&repo_path), git_buf_cstr(&wd_path), opts);
1158
+
1159
+ /* TODO: reinitialize the templates */
1160
+ }
1161
+ else {
1162
+ if (!(error = repo_init_structure(
1163
+ git_buf_cstr(&repo_path), git_buf_cstr(&wd_path), opts)) &&
1164
+ !(error = repo_init_config(
1165
+ git_buf_cstr(&repo_path), git_buf_cstr(&wd_path), opts)))
1166
+ error = repo_init_create_head(
1167
+ git_buf_cstr(&repo_path), opts->initial_head);
866
1168
  }
1169
+ if (error < 0)
1170
+ goto cleanup;
867
1171
 
868
- result = git_repository_open(repo_out, repository_path.ptr);
1172
+ error = git_repository_open(repo_out, git_buf_cstr(&repo_path));
1173
+
1174
+ if (!error && opts->origin_url)
1175
+ error = repo_init_create_origin(*repo_out, opts->origin_url);
869
1176
 
870
1177
  cleanup:
871
- git_buf_free(&repository_path);
872
- return result;
1178
+ git_buf_free(&repo_path);
1179
+ git_buf_free(&wd_path);
1180
+
1181
+ return error;
873
1182
  }
874
1183
 
875
1184
  int git_repository_head_detached(git_repository *repo)
@@ -897,7 +1206,11 @@ int git_repository_head_detached(git_repository *repo)
897
1206
 
898
1207
  int git_repository_head(git_reference **head_out, git_repository *repo)
899
1208
  {
900
- return git_reference_lookup_resolved(head_out, repo, GIT_HEAD_FILE, -1);
1209
+ int error;
1210
+
1211
+ error = git_reference_lookup_resolved(head_out, repo, GIT_HEAD_FILE, -1);
1212
+
1213
+ return error == GIT_ENOTFOUND ? GIT_EORPHANEDHEAD : error;
901
1214
  }
902
1215
 
903
1216
  int git_repository_head_orphan(git_repository *repo)
@@ -908,7 +1221,7 @@ int git_repository_head_orphan(git_repository *repo)
908
1221
  error = git_repository_head(&ref, repo);
909
1222
  git_reference_free(ref);
910
1223
 
911
- if (error == GIT_ENOTFOUND)
1224
+ if (error == GIT_EORPHANEDHEAD)
912
1225
  return 1;
913
1226
 
914
1227
  if (error < 0)
@@ -922,7 +1235,7 @@ int git_repository_is_empty(git_repository *repo)
922
1235
  git_reference *head = NULL, *branch = NULL;
923
1236
  int error;
924
1237
 
925
- if (git_reference_lookup(&head, repo, "HEAD") < 0)
1238
+ if (git_reference_lookup(&head, repo, GIT_HEAD_FILE) < 0)
926
1239
  return -1;
927
1240
 
928
1241
  if (git_reference_type(head) != GIT_REF_SYMBOLIC) {
@@ -930,7 +1243,7 @@ int git_repository_is_empty(git_repository *repo)
930
1243
  return 0;
931
1244
  }
932
1245
 
933
- if (strcmp(git_reference_target(head), "refs/heads/master") != 0) {
1246
+ if (strcmp(git_reference_target(head), GIT_REFS_HEADS_DIR "master") != 0) {
934
1247
  git_reference_free(head);
935
1248
  return 0;
936
1249
  }
@@ -965,43 +1278,6 @@ const char *git_repository_workdir(git_repository *repo)
965
1278
  return repo->workdir;
966
1279
  }
967
1280
 
968
- static int write_gitlink(
969
- const char *in_dir, const char *to_repo)
970
- {
971
- int error;
972
- git_buf buf = GIT_BUF_INIT;
973
- struct stat st;
974
-
975
- if (git_path_dirname_r(&buf, to_repo) < 0 ||
976
- git_path_to_dir(&buf) < 0)
977
- return -1;
978
-
979
- /* don't write gitlink to natural workdir */
980
- if (git__suffixcmp(to_repo, "/" DOT_GIT "/") == 0 &&
981
- strcmp(in_dir, buf.ptr) == 0)
982
- return GIT_PASSTHROUGH;
983
-
984
- if (git_buf_joinpath(&buf, in_dir, DOT_GIT) < 0)
985
- return -1;
986
-
987
- if (!p_stat(buf.ptr, &st) && !S_ISREG(st.st_mode)) {
988
- giterr_set(GITERR_REPOSITORY,
989
- "Cannot overwrite gitlink file into path '%s'", in_dir);
990
- return GIT_EEXISTS;
991
- }
992
-
993
- git_buf_clear(&buf);
994
-
995
- if (git_buf_printf(&buf, "%s %s", GIT_FILE_CONTENT_PREFIX, to_repo) < 0)
996
- return -1;
997
-
998
- error = repo_write_template(in_dir, true, DOT_GIT, 0644, buf.ptr);
999
-
1000
- git_buf_free(&buf);
1001
-
1002
- return error;
1003
- }
1004
-
1005
1281
  int git_repository_set_workdir(
1006
1282
  git_repository *repo, const char *workdir, int update_gitlink)
1007
1283
  {
@@ -1022,7 +1298,7 @@ int git_repository_set_workdir(
1022
1298
  if (git_repository_config__weakptr(&config, repo) < 0)
1023
1299
  return -1;
1024
1300
 
1025
- error = write_gitlink(path.ptr, git_repository_path(repo));
1301
+ error = repo_write_gitlink(path.ptr, git_repository_path(repo));
1026
1302
 
1027
1303
  /* passthrough error means gitlink is unnecessary */
1028
1304
  if (error == GIT_PASSTHROUGH)
@@ -1078,39 +1354,27 @@ int git_repository_message(char *buffer, size_t len, git_repository *repo)
1078
1354
  {
1079
1355
  git_buf buf = GIT_BUF_INIT, path = GIT_BUF_INIT;
1080
1356
  struct stat st;
1081
- ssize_t size;
1082
1357
  int error;
1083
1358
 
1084
1359
  if (git_buf_joinpath(&path, repo->path_repository, MERGE_MSG_FILE) < 0)
1085
1360
  return -1;
1086
1361
 
1087
- error = p_stat(git_buf_cstr(&path), &st);
1088
- if (error < 0) {
1362
+ if ((error = p_stat(git_buf_cstr(&path), &st)) < 0) {
1089
1363
  if (errno == ENOENT)
1090
1364
  error = GIT_ENOTFOUND;
1091
-
1092
- git_buf_free(&path);
1093
- return error;
1094
1365
  }
1095
-
1096
- if (buffer == NULL) {
1097
- git_buf_free(&path);
1098
- return (int)st.st_size;
1366
+ else if (buffer != NULL) {
1367
+ error = git_futils_readbuffer(&buf, git_buf_cstr(&path));
1368
+ git_buf_copy_cstr(buffer, len, &buf);
1099
1369
  }
1100
1370
 
1101
- if (git_futils_readbuffer(&buf, git_buf_cstr(&path)) < 0)
1102
- goto on_error;
1103
-
1104
- memcpy(buffer, git_buf_cstr(&buf), len);
1105
- size = git_buf_len(&buf);
1106
-
1107
1371
  git_buf_free(&path);
1108
1372
  git_buf_free(&buf);
1109
- return size;
1110
1373
 
1111
- on_error:
1112
- git_buf_free(&path);
1113
- return -1;
1374
+ if (!error)
1375
+ error = (int)st.st_size + 1; /* add 1 for NUL byte */
1376
+
1377
+ return error;
1114
1378
  }
1115
1379
 
1116
1380
  int git_repository_message_remove(git_repository *repo)
@@ -1126,3 +1390,154 @@ int git_repository_message_remove(git_repository *repo)
1126
1390
 
1127
1391
  return error;
1128
1392
  }
1393
+
1394
+ int git_repository_hashfile(
1395
+ git_oid *out,
1396
+ git_repository *repo,
1397
+ const char *path,
1398
+ git_otype type,
1399
+ const char *as_path)
1400
+ {
1401
+ int error;
1402
+ git_vector filters = GIT_VECTOR_INIT;
1403
+ git_file fd = -1;
1404
+ git_off_t len;
1405
+ git_buf full_path = GIT_BUF_INIT;
1406
+
1407
+ assert(out && path && repo); /* as_path can be NULL */
1408
+
1409
+ /* At some point, it would be nice if repo could be NULL to just
1410
+ * apply filter rules defined in system and global files, but for
1411
+ * now that is not possible because git_filters_load() needs it.
1412
+ */
1413
+
1414
+ error = git_path_join_unrooted(
1415
+ &full_path, path, repo ? git_repository_workdir(repo) : NULL, NULL);
1416
+ if (error < 0)
1417
+ return error;
1418
+
1419
+ if (!as_path)
1420
+ as_path = path;
1421
+
1422
+ /* passing empty string for "as_path" indicated --no-filters */
1423
+ if (strlen(as_path) > 0) {
1424
+ error = git_filters_load(&filters, repo, as_path, GIT_FILTER_TO_ODB);
1425
+ if (error < 0)
1426
+ return error;
1427
+ } else {
1428
+ error = 0;
1429
+ }
1430
+
1431
+ /* at this point, error is a count of the number of loaded filters */
1432
+
1433
+ fd = git_futils_open_ro(full_path.ptr);
1434
+ if (fd < 0) {
1435
+ error = fd;
1436
+ goto cleanup;
1437
+ }
1438
+
1439
+ len = git_futils_filesize(fd);
1440
+ if (len < 0) {
1441
+ error = (int)len;
1442
+ goto cleanup;
1443
+ }
1444
+
1445
+ if (!git__is_sizet(len)) {
1446
+ giterr_set(GITERR_OS, "File size overflow for 32-bit systems");
1447
+ error = -1;
1448
+ goto cleanup;
1449
+ }
1450
+
1451
+ error = git_odb__hashfd_filtered(out, fd, (size_t)len, type, &filters);
1452
+
1453
+ cleanup:
1454
+ if (fd >= 0)
1455
+ p_close(fd);
1456
+ git_filters_free(&filters);
1457
+ git_buf_free(&full_path);
1458
+
1459
+ return error;
1460
+ }
1461
+
1462
+ static bool looks_like_a_branch(const char *refname)
1463
+ {
1464
+ return git__prefixcmp(refname, GIT_REFS_HEADS_DIR) == 0;
1465
+ }
1466
+
1467
+ int git_repository_set_head(
1468
+ git_repository* repo,
1469
+ const char* refname)
1470
+ {
1471
+ git_reference *ref,
1472
+ *new_head = NULL;
1473
+ int error;
1474
+
1475
+ assert(repo && refname);
1476
+
1477
+ error = git_reference_lookup(&ref, repo, refname);
1478
+ if (error < 0 && error != GIT_ENOTFOUND)
1479
+ return error;
1480
+
1481
+ if (!error) {
1482
+ if (git_reference_is_branch(ref))
1483
+ error = git_reference_create_symbolic(&new_head, repo, GIT_HEAD_FILE, git_reference_name(ref), 1);
1484
+ else
1485
+ error = git_repository_set_head_detached(repo, git_reference_oid(ref));
1486
+ } else if (looks_like_a_branch(refname))
1487
+ error = git_reference_create_symbolic(&new_head, repo, GIT_HEAD_FILE, refname, 1);
1488
+
1489
+ git_reference_free(ref);
1490
+ git_reference_free(new_head);
1491
+ return error;
1492
+ }
1493
+
1494
+ int git_repository_set_head_detached(
1495
+ git_repository* repo,
1496
+ const git_oid* commitish)
1497
+ {
1498
+ int error;
1499
+ git_object *object,
1500
+ *peeled = NULL;
1501
+ git_reference *new_head = NULL;
1502
+
1503
+ assert(repo && commitish);
1504
+
1505
+ if ((error = git_object_lookup(&object, repo, commitish, GIT_OBJ_ANY)) < 0)
1506
+ return error;
1507
+
1508
+ if ((error = git_object_peel(&peeled, object, GIT_OBJ_COMMIT)) < 0)
1509
+ goto cleanup;
1510
+
1511
+ error = git_reference_create_oid(&new_head, repo, GIT_HEAD_FILE, git_object_id(peeled), 1);
1512
+
1513
+ cleanup:
1514
+ git_object_free(object);
1515
+ git_object_free(peeled);
1516
+ git_reference_free(new_head);
1517
+ return error;
1518
+ }
1519
+
1520
+ int git_repository_detach_head(
1521
+ git_repository* repo)
1522
+ {
1523
+ git_reference *old_head = NULL,
1524
+ *new_head = NULL;
1525
+ git_object *object = NULL;
1526
+ int error;
1527
+
1528
+ assert(repo);
1529
+
1530
+ if ((error = git_repository_head(&old_head, repo)) < 0)
1531
+ return error;
1532
+
1533
+ if ((error = git_object_lookup(&object, repo, git_reference_oid(old_head), GIT_OBJ_COMMIT)) < 0)
1534
+ goto cleanup;
1535
+
1536
+ error = git_reference_create_oid(&new_head, repo, GIT_HEAD_FILE, git_reference_oid(old_head), 1);
1537
+
1538
+ cleanup:
1539
+ git_object_free(object);
1540
+ git_reference_free(old_head);
1541
+ git_reference_free(new_head);
1542
+ return error;
1543
+ }