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
@@ -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
+ }