eac_git 0.1.0 → 0.2.0

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 (190) hide show
  1. checksums.yaml +4 -4
  2. data/lib/eac_git/executables.rb +24 -4
  3. data/lib/eac_git/rspec.rb +17 -0
  4. data/lib/eac_git/version.rb +1 -1
  5. data/vendor/git-subrepo/Changes +110 -0
  6. data/vendor/git-subrepo/Intro.pod +509 -0
  7. data/vendor/git-subrepo/License +21 -0
  8. data/vendor/git-subrepo/Makefile +82 -0
  9. data/vendor/git-subrepo/Meta +28 -0
  10. data/vendor/git-subrepo/ReadMe.pod +698 -0
  11. data/vendor/git-subrepo/doc/comparison.swim +35 -0
  12. data/vendor/git-subrepo/doc/git-subrepo.swim +608 -0
  13. data/vendor/git-subrepo/doc/intro-to-subrepo.swim +387 -0
  14. data/vendor/git-subrepo/ext/bashplus/Changes +15 -0
  15. data/vendor/git-subrepo/ext/bashplus/License +21 -0
  16. data/vendor/git-subrepo/ext/bashplus/Makefile +45 -0
  17. data/vendor/git-subrepo/ext/bashplus/Meta +28 -0
  18. data/vendor/git-subrepo/ext/bashplus/ReadMe.pod +77 -0
  19. data/vendor/git-subrepo/ext/bashplus/bin/bash+ +43 -0
  20. data/vendor/git-subrepo/ext/bashplus/doc/bash+.swim +61 -0
  21. data/vendor/git-subrepo/ext/bashplus/lib/bash+.bash +92 -0
  22. data/vendor/git-subrepo/ext/bashplus/man/man1/bash+.1 +134 -0
  23. data/vendor/git-subrepo/ext/bashplus/man/man3/bash+.3 +134 -0
  24. data/vendor/git-subrepo/ext/bashplus/test/base.t +12 -0
  25. data/vendor/git-subrepo/ext/bashplus/test/fcopy.t +22 -0
  26. data/vendor/git-subrepo/ext/bashplus/test/lib/foo/bar.bash +3 -0
  27. data/vendor/git-subrepo/ext/bashplus/test/lib/foo/foo.bash +3 -0
  28. data/vendor/git-subrepo/ext/bashplus/test/source-bash+-std.t +18 -0
  29. data/vendor/git-subrepo/ext/bashplus/test/source-bash+.t +23 -0
  30. data/vendor/git-subrepo/ext/bashplus/test/test.bash +70 -0
  31. data/vendor/git-subrepo/ext/bashplus/test/use.t +19 -0
  32. data/vendor/git-subrepo/ext/test-more-bash/Changes +15 -0
  33. data/vendor/git-subrepo/ext/test-more-bash/License +21 -0
  34. data/vendor/git-subrepo/ext/test-more-bash/Makefile +20 -0
  35. data/vendor/git-subrepo/ext/test-more-bash/Meta +30 -0
  36. data/vendor/git-subrepo/ext/test-more-bash/ReadMe.pod +115 -0
  37. data/vendor/git-subrepo/ext/test-more-bash/doc/test-more.swim +89 -0
  38. data/vendor/git-subrepo/ext/test-more-bash/ext/bashplus/Changes +15 -0
  39. data/vendor/git-subrepo/ext/test-more-bash/ext/bashplus/License +21 -0
  40. data/vendor/git-subrepo/ext/test-more-bash/ext/bashplus/Makefile +45 -0
  41. data/vendor/git-subrepo/ext/test-more-bash/ext/bashplus/Meta +28 -0
  42. data/vendor/git-subrepo/ext/test-more-bash/ext/bashplus/ReadMe.pod +77 -0
  43. data/vendor/git-subrepo/ext/test-more-bash/ext/bashplus/bin/bash+ +43 -0
  44. data/vendor/git-subrepo/ext/test-more-bash/ext/bashplus/doc/bash+.swim +61 -0
  45. data/vendor/git-subrepo/ext/test-more-bash/ext/bashplus/lib/bash+.bash +92 -0
  46. data/vendor/git-subrepo/ext/test-more-bash/ext/bashplus/man/man1/bash+.1 +134 -0
  47. data/vendor/git-subrepo/ext/test-more-bash/ext/bashplus/man/man3/bash+.3 +134 -0
  48. data/vendor/git-subrepo/ext/test-more-bash/ext/bashplus/test/base.t +12 -0
  49. data/vendor/git-subrepo/ext/test-more-bash/ext/bashplus/test/fcopy.t +22 -0
  50. data/vendor/git-subrepo/ext/test-more-bash/ext/bashplus/test/lib/foo/bar.bash +3 -0
  51. data/vendor/git-subrepo/ext/test-more-bash/ext/bashplus/test/lib/foo/foo.bash +3 -0
  52. data/vendor/git-subrepo/ext/test-more-bash/ext/bashplus/test/source-bash+-std.t +18 -0
  53. data/vendor/git-subrepo/ext/test-more-bash/ext/bashplus/test/source-bash+.t +23 -0
  54. data/vendor/git-subrepo/ext/test-more-bash/ext/bashplus/test/test.bash +70 -0
  55. data/vendor/git-subrepo/ext/test-more-bash/ext/bashplus/test/use.t +19 -0
  56. data/vendor/git-subrepo/ext/test-more-bash/ext/test-tap-bash/Changes +15 -0
  57. data/vendor/git-subrepo/ext/test-more-bash/ext/test-tap-bash/License +21 -0
  58. data/vendor/git-subrepo/ext/test-more-bash/ext/test-tap-bash/Makefile +37 -0
  59. data/vendor/git-subrepo/ext/test-more-bash/ext/test-tap-bash/Meta +28 -0
  60. data/vendor/git-subrepo/ext/test-more-bash/ext/test-tap-bash/ReadMe.pod +66 -0
  61. data/vendor/git-subrepo/ext/test-more-bash/ext/test-tap-bash/doc/test-tap.swim +48 -0
  62. data/vendor/git-subrepo/ext/test-more-bash/ext/test-tap-bash/lib/test/tap.bash +153 -0
  63. data/vendor/git-subrepo/ext/test-more-bash/ext/test-tap-bash/man/man3/test-tap.3 +119 -0
  64. data/vendor/git-subrepo/ext/test-more-bash/ext/test-tap-bash/test/bail_out.t +13 -0
  65. data/vendor/git-subrepo/ext/test-more-bash/ext/test-tap-bash/test/done.t +10 -0
  66. data/vendor/git-subrepo/ext/test-more-bash/ext/test-tap-bash/test/fail.t +20 -0
  67. data/vendor/git-subrepo/ext/test-more-bash/ext/test-tap-bash/test/fail_fast.t +15 -0
  68. data/vendor/git-subrepo/ext/test-more-bash/ext/test-tap-bash/test/helper.bash +9 -0
  69. data/vendor/git-subrepo/ext/test-more-bash/ext/test-tap-bash/test/pass.t +9 -0
  70. data/vendor/git-subrepo/ext/test-more-bash/ext/test-tap-bash/test/plan.t +10 -0
  71. data/vendor/git-subrepo/ext/test-more-bash/ext/test-tap-bash/test/skip_all.t +20 -0
  72. data/vendor/git-subrepo/ext/test-more-bash/ext/test-tap-bash/test/tap.t +13 -0
  73. data/vendor/git-subrepo/ext/test-more-bash/ext/test-tap-bash/test/test/bail.t +14 -0
  74. data/vendor/git-subrepo/ext/test-more-bash/ext/test-tap-bash/test/test/fail.t +7 -0
  75. data/vendor/git-subrepo/ext/test-more-bash/ext/test-tap-bash/test/test/fail_fast.t +12 -0
  76. data/vendor/git-subrepo/ext/test-more-bash/ext/test-tap-bash/test/test/skip-all-init.t +8 -0
  77. data/vendor/git-subrepo/ext/test-more-bash/ext/test-tap-bash/test/test/skip-all-plan.t +9 -0
  78. data/vendor/git-subrepo/ext/test-more-bash/lib/test/more.bash +95 -0
  79. data/vendor/git-subrepo/ext/test-more-bash/man/man3/test-more.3 +173 -0
  80. data/vendor/git-subrepo/ext/test-more-bash/test/fail.t +20 -0
  81. data/vendor/git-subrepo/ext/test-more-bash/test/more.t +20 -0
  82. data/vendor/git-subrepo/ext/test-more-bash/test/pass.t +9 -0
  83. data/vendor/git-subrepo/ext/test-more-bash/test/setup +8 -0
  84. data/vendor/git-subrepo/ext/test-more-bash/test/skip_all.t +11 -0
  85. data/vendor/git-subrepo/ext/test-more-bash/test/test/fail1.t +12 -0
  86. data/vendor/git-subrepo/ext/test-more-bash/test/test/skip_all.t +10 -0
  87. data/vendor/git-subrepo/lib/git-subrepo +1901 -0
  88. data/vendor/git-subrepo/lib/git-subrepo.d/bash+.bash +1 -0
  89. data/vendor/git-subrepo/lib/git-subrepo.d/help-functions.bash +339 -0
  90. data/vendor/git-subrepo/man/man1/git-subrepo.1 +743 -0
  91. data/vendor/git-subrepo/note/0.4.0 +12 -0
  92. data/vendor/git-subrepo/note/AllGitCmds +148 -0
  93. data/vendor/git-subrepo/note/Cases +32 -0
  94. data/vendor/git-subrepo/note/Commands +33 -0
  95. data/vendor/git-subrepo/note/Dags +199 -0
  96. data/vendor/git-subrepo/note/Gists +7 -0
  97. data/vendor/git-subrepo/note/Links +25 -0
  98. data/vendor/git-subrepo/note/Plugins +10 -0
  99. data/vendor/git-subrepo/note/Spec +39 -0
  100. data/vendor/git-subrepo/note/Story1 +57 -0
  101. data/vendor/git-subrepo/note/ToDo +55 -0
  102. data/vendor/git-subrepo/note/design.swim +137 -0
  103. data/vendor/git-subrepo/note/design2.swim +85 -0
  104. data/vendor/git-subrepo/note/init-test +38 -0
  105. data/vendor/git-subrepo/note/pull-dance.txt +18 -0
  106. data/vendor/git-subrepo/note/recreate-rebase-conflict.sh +56 -0
  107. data/vendor/git-subrepo/note/subtree-rebase-fail-example/test.bash +29 -0
  108. data/vendor/git-subrepo/note/test-subrepo-push.sh +69 -0
  109. data/vendor/git-subrepo/note/test.sh +58 -0
  110. data/vendor/git-subrepo/pkg/bin/generate-completion.pl +210 -0
  111. data/vendor/git-subrepo/pkg/bin/generate-help-functions.pl +89 -0
  112. data/vendor/git-subrepo/share/completion.bash +42 -0
  113. data/vendor/git-subrepo/share/enable-completion.sh +50 -0
  114. data/vendor/git-subrepo/share/git-completion.bash +2738 -0
  115. data/vendor/git-subrepo/share/zsh-completion/_git-subrepo +81 -0
  116. data/vendor/git-subrepo/test/branch-all.t +41 -0
  117. data/vendor/git-subrepo/test/branch-rev-list-one-path.t +43 -0
  118. data/vendor/git-subrepo/test/branch-rev-list.t +47 -0
  119. data/vendor/git-subrepo/test/branch.t +52 -0
  120. data/vendor/git-subrepo/test/clean.t +43 -0
  121. data/vendor/git-subrepo/test/clone-annotated-tag.t +45 -0
  122. data/vendor/git-subrepo/test/clone.t +107 -0
  123. data/vendor/git-subrepo/test/compile.t +19 -0
  124. data/vendor/git-subrepo/test/config.t +58 -0
  125. data/vendor/git-subrepo/test/encode.t +91 -0
  126. data/vendor/git-subrepo/test/error.t +171 -0
  127. data/vendor/git-subrepo/test/fetch.t +43 -0
  128. data/vendor/git-subrepo/test/gitignore.t +61 -0
  129. data/vendor/git-subrepo/test/init.t +64 -0
  130. data/vendor/git-subrepo/test/issue29.t +98 -0
  131. data/vendor/git-subrepo/test/issue95.t +98 -0
  132. data/vendor/git-subrepo/test/issue96.t +96 -0
  133. data/vendor/git-subrepo/test/pull-all.t +38 -0
  134. data/vendor/git-subrepo/test/pull-merge.t +113 -0
  135. data/vendor/git-subrepo/test/pull-message.t +88 -0
  136. data/vendor/git-subrepo/test/pull-new-branch.t +58 -0
  137. data/vendor/git-subrepo/test/pull-ours.t +90 -0
  138. data/vendor/git-subrepo/test/pull-theirs.t +82 -0
  139. data/vendor/git-subrepo/test/pull-twice.t +44 -0
  140. data/vendor/git-subrepo/test/pull-worktree.t +40 -0
  141. data/vendor/git-subrepo/test/pull.t +99 -0
  142. data/vendor/git-subrepo/test/push-after-init.t +51 -0
  143. data/vendor/git-subrepo/test/push-force.t +56 -0
  144. data/vendor/git-subrepo/test/push-new-branch.t +61 -0
  145. data/vendor/git-subrepo/test/push-no-changes.t +29 -0
  146. data/vendor/git-subrepo/test/push-squash.t +56 -0
  147. data/vendor/git-subrepo/test/push.t +176 -0
  148. data/vendor/git-subrepo/test/reclone.t +45 -0
  149. data/vendor/git-subrepo/test/repo/bar/HEAD +1 -0
  150. data/vendor/git-subrepo/test/repo/bar/config +4 -0
  151. data/vendor/git-subrepo/test/repo/bar/objects/1f/0c4b264caed0126814a0ede851a1e0b4e16ae6 +0 -0
  152. data/vendor/git-subrepo/test/repo/bar/objects/87/46903fdb1b9c2101377880125917c2e05b4d69 +0 -0
  153. data/vendor/git-subrepo/test/repo/bar/objects/94/c86ffc745232d89f78c6f895e11e71272518db +0 -0
  154. data/vendor/git-subrepo/test/repo/bar/objects/c6/76c57b6576743fa56278527aa60ebd2e202a7c +0 -0
  155. data/vendor/git-subrepo/test/repo/bar/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 +0 -0
  156. data/vendor/git-subrepo/test/repo/bar/objects/f6/2a8ff3feadf39b0a98f1a86ec6d1eb33858ee9 +0 -0
  157. data/vendor/git-subrepo/test/repo/bar/refs/heads/master +1 -0
  158. data/vendor/git-subrepo/test/repo/bar/refs/tags/A +1 -0
  159. data/vendor/git-subrepo/test/repo/foo/HEAD +1 -0
  160. data/vendor/git-subrepo/test/repo/foo/config +4 -0
  161. data/vendor/git-subrepo/test/repo/foo/objects/a0/f4cdaaf533a936296cdebbed8206c3b9ededa8 +0 -0
  162. data/vendor/git-subrepo/test/repo/foo/objects/e2/1291a1ad392a9d4c51dd9586804f1467b28afd +0 -0
  163. data/vendor/git-subrepo/test/repo/foo/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 +0 -0
  164. data/vendor/git-subrepo/test/repo/foo/refs/heads/master +1 -0
  165. data/vendor/git-subrepo/test/repo/init/HEAD +1 -0
  166. data/vendor/git-subrepo/test/repo/init/config +5 -0
  167. data/vendor/git-subrepo/test/repo/init/objects/11/523f5dcf03b4c89b592dc8a3d0308f68da2386 +0 -0
  168. data/vendor/git-subrepo/test/repo/init/objects/14/2addf8ec5f37334e837440122c62f2c68a29ad +0 -0
  169. data/vendor/git-subrepo/test/repo/init/objects/32/5180321750a21cd7a4e7ecda319e557a4f6a09 +2 -0
  170. data/vendor/git-subrepo/test/repo/init/objects/3d/918c6901c02f43af5d31779dd5e1f9166aeb36 +0 -0
  171. data/vendor/git-subrepo/test/repo/init/objects/3e/4cb596066dce63ba4d047abddb677389b65e19 +0 -0
  172. data/vendor/git-subrepo/test/repo/init/objects/4b/6e53022e7a04f07887697e4f3d7c377fd9822b +0 -0
  173. data/vendor/git-subrepo/test/repo/init/objects/58/931fc1bd559b59c41ea738fc7ad04f9ad01bd3 +0 -0
  174. data/vendor/git-subrepo/test/repo/init/objects/5e/c0c28e1b806f25efdca18fcf7a74b49c3755bd +0 -0
  175. data/vendor/git-subrepo/test/repo/init/objects/75/fa6584e748f57eff06eebdc55e9ac21d4fcbf2 +1 -0
  176. data/vendor/git-subrepo/test/repo/init/objects/80/2d5edbd5e1cb7fca82b5bd38e7c8a0a496fb20 +0 -0
  177. data/vendor/git-subrepo/test/repo/init/objects/94/7b3d714c38791e95ad6f928b48c98bb8708acd +0 -0
  178. data/vendor/git-subrepo/test/repo/init/objects/95/e1f2df3f4d5f3d7a60588c25a7ca8a913d3c2a +1 -0
  179. data/vendor/git-subrepo/test/repo/init/objects/b1/5f4a7666baf40d949548ead946a3370e273479 +0 -0
  180. data/vendor/git-subrepo/test/repo/init/objects/c3/ee8978c4c5d84c3b7d00ba8e5906933d027882 +0 -0
  181. data/vendor/git-subrepo/test/repo/init/objects/c8/b0bffbc405ef3fad7354ff833fbec36d67ddfa +3 -0
  182. data/vendor/git-subrepo/test/repo/init/objects/dd/8bdb934ec848137f011fe423b185505c343626 +2 -0
  183. data/vendor/git-subrepo/test/repo/init/objects/e2/9be58c767cfeb27235c995d293a7d71aac0135 +2 -0
  184. data/vendor/git-subrepo/test/repo/init/objects/ee/1224401fc6aac595145fa727dcf6706ac8aec1 +1 -0
  185. data/vendor/git-subrepo/test/repo/init/objects/f1/cc1a657b2e805c400f5dcaaa76bd29c6178b1b +0 -0
  186. data/vendor/git-subrepo/test/repo/init/refs/heads/master +1 -0
  187. data/vendor/git-subrepo/test/setup +205 -0
  188. data/vendor/git-subrepo/test/status.t +68 -0
  189. data/vendor/git-subrepo/test/submodule.t +45 -0
  190. metadata +188 -2
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env bash
2
+
3
+ source lib/test/tap.bash
4
+
5
+ Test::Tap:init tests 5
6
+
7
+ Test::Tap:pass 'test #1'
8
+ Test::Tap:pass 'test #2'
9
+ Test::Tap:pass 'test #3'
10
+
11
+ Test::Tap:BAIL_OUT 'Get me outta here'
12
+
13
+ Test::Tap:pass 'test #4'
14
+ Test::Tap:fail 'test #5'
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env bash
2
+
3
+ source lib/test/tap.bash
4
+
5
+ Test::Tap:init tests 1
6
+
7
+ Test::Tap:fail 'I am a failure'
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env bash
2
+
3
+ source lib/test/tap.bash
4
+
5
+ Test::Tap:init tests 5
6
+ Test::Tap:BAIL_ON_FAIL
7
+
8
+ Test::Tap:pass 'test #1'
9
+ Test::Tap:pass 'test #2'
10
+ Test::Tap:fail 'test #3'
11
+ Test::Tap:pass 'test #4'
12
+ Test::Tap:pass 'test #5'
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+
3
+ source lib/test/tap.bash
4
+
5
+ Test::Tap:init skip_all 'Test for skip_all from init'
6
+
7
+ Test::Tap:diag "This code should not be run"
8
+ Test::Tap:fail
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env bash
2
+
3
+ source lib/test/tap.bash
4
+
5
+ Test::Tap:init
6
+ Test::Tap:plan skip_all 'Test for skip_all from plan'
7
+
8
+ Test::Tap:diag "This code should not be run"
9
+ Test::Tap:fail
@@ -0,0 +1,95 @@
1
+ # test/more.bash - Complete TAP test framework for Bash
2
+ #
3
+ # Copyright (c) 2013-2016. Ingy döt Net.
4
+
5
+ set -e
6
+
7
+ Test__More_VERSION=0.0.3
8
+
9
+ source bash+ :std
10
+ use Test::Tap
11
+
12
+ Test::More:import() { Test::Tap:init "$@"; }
13
+
14
+ plan() { Test::Tap:plan "$@"; }
15
+ pass() { Test::Tap:pass "$@"; }
16
+ fail() { Test::Tap:fail "$@"; }
17
+ diag() { Test::Tap:diag "$@"; }
18
+ note() { Test::Tap:note "$@"; }
19
+ done_testing() { Test::Tap:done_testing "$@"; }
20
+ BAIL_OUT() { Test::Tap:BAIL_OUT "$@"; }
21
+ BAIL_ON_FAIL() { Test::Tap:BAIL_ON_FAIL "$@"; }
22
+
23
+ is() {
24
+ local got="$1" want="$2" label="$3"
25
+ if [[ $got == "$want" ]]; then
26
+ Test::Tap:pass "$label"
27
+ else
28
+ Test::Tap:fail "$label" Test::More:is-fail
29
+ fi
30
+ }
31
+
32
+ Test::More:is-fail() {
33
+ local Test__Tap_CALL_STACK_LEVEL=
34
+ Test__Tap_CALL_STACK_LEVEL=$(( Test__Tap_CALL_STACK_LEVEL + 1 ))
35
+ if [[ "$want" =~ \n ]]; then
36
+ echo "$got" > /tmp/got-$$
37
+ echo "$want" > /tmp/want-$$
38
+ diff -u /tmp/{want,got}-$$ >&2
39
+ wc /tmp/{want,got}-$$ >&2
40
+ rm -f /tmp/{got,want}-$$
41
+ else
42
+ Test::Tap:diag "\
43
+ got: '$got'
44
+ expected: '$want'"
45
+ fi
46
+ }
47
+
48
+ isnt() {
49
+ local Test__Tap_CALL_STACK_LEVEL=
50
+ Test__Tap_CALL_STACK_LEVEL=$(( Test__Tap_CALL_STACK_LEVEL + 1 ))
51
+ local got="$1" dontwant="$2" label="$3"
52
+ if [[ $got != "$dontwant" ]]; then
53
+ Test::Tap:pass "$label"
54
+ else
55
+ Test::Tap:fail "$label" Test::More:isnt-fail
56
+ fi
57
+ }
58
+
59
+ Test::More:isnt-fail() {
60
+ Test::Tap:diag "\
61
+ got: '$got'
62
+ expected: anything else"
63
+ }
64
+
65
+ ok() {
66
+ (exit ${1:-$?}) &&
67
+ Test::Tap:pass "$2" ||
68
+ Test::Tap:fail "$2"
69
+ }
70
+
71
+ like() {
72
+ local got=$1 regex=$2 label=$3
73
+ if [[ $got =~ "$regex" ]]; then
74
+ Test::Tap:pass "$label"
75
+ else
76
+ Test::Tap:fail "$label" Test::More:like-fail
77
+ fi
78
+ }
79
+
80
+ Test::More:like-fail() {
81
+ Test::Tap:diag "Got: '$got'"
82
+ }
83
+
84
+ unlike() {
85
+ local got=$1 regex=$2 label=$3
86
+ if [[ ! $got =~ "$regex" ]]; then
87
+ Test::Tap:pass "$label"
88
+ else
89
+ Test::Tap:fail "$label" Test::More:unlike-fail
90
+ fi
91
+ }
92
+
93
+ Test::More:unlike-fail() {
94
+ Test::Tap:diag "Got: '$got'"
95
+ }
@@ -0,0 +1,173 @@
1
+ .\" Automatically generated by Pod::Man 2.27 (Pod::Simple 3.28)
2
+ .\"
3
+ .\" Standard preamble:
4
+ .\" ========================================================================
5
+ .de Sp \" Vertical space (when we can't use .PP)
6
+ .if t .sp .5v
7
+ .if n .sp
8
+ ..
9
+ .de Vb \" Begin verbatim text
10
+ .ft CW
11
+ .nf
12
+ .ne \\$1
13
+ ..
14
+ .de Ve \" End verbatim text
15
+ .ft R
16
+ .fi
17
+ ..
18
+ .\" Set up some character translations and predefined strings. \*(-- will
19
+ .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left
20
+ .\" double quote, and \*(R" will give a right double quote. \*(C+ will
21
+ .\" give a nicer C++. Capital omega is used to do unbreakable dashes and
22
+ .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff,
23
+ .\" nothing in troff, for use with C<>.
24
+ .tr \(*W-
25
+ .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p'
26
+ .ie n \{\
27
+ . ds -- \(*W-
28
+ . ds PI pi
29
+ . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch
30
+ . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch
31
+ . ds L" ""
32
+ . ds R" ""
33
+ . ds C` ""
34
+ . ds C' ""
35
+ 'br\}
36
+ .el\{\
37
+ . ds -- \|\(em\|
38
+ . ds PI \(*p
39
+ . ds L" ``
40
+ . ds R" ''
41
+ . ds C`
42
+ . ds C'
43
+ 'br\}
44
+ .\"
45
+ .\" Escape single quotes in literal strings from groff's Unicode transform.
46
+ .ie \n(.g .ds Aq \(aq
47
+ .el .ds Aq '
48
+ .\"
49
+ .\" If the F register is turned on, we'll generate index entries on stderr for
50
+ .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index
51
+ .\" entries marked with X<> in POD. Of course, you'll have to process the
52
+ .\" output yourself in some meaningful fashion.
53
+ .\"
54
+ .\" Avoid warning from groff about undefined register 'F'.
55
+ .de IX
56
+ ..
57
+ .nr rF 0
58
+ .if \n(.g .if rF .nr rF 1
59
+ .if (\n(rF:(\n(.g==0)) \{
60
+ . if \nF \{
61
+ . de IX
62
+ . tm Index:\\$1\t\\n%\t"\\$2"
63
+ ..
64
+ . if !\nF==2 \{
65
+ . nr % 0
66
+ . nr F 2
67
+ . \}
68
+ . \}
69
+ .\}
70
+ .rr rF
71
+ .\" ========================================================================
72
+ .\"
73
+ .IX Title "Test::More 1"
74
+ .TH Test::More 1 "January 2016" "Generated by Swim v0.1.41" "\s-1TAP\s0 Testing for Bash"
75
+ .\" For nroff, turn off justification. Always turn off hyphenation; it makes
76
+ .\" way too many mistakes in technical documents.
77
+ .if n .ad l
78
+ .nh
79
+ .SH "Name"
80
+ .IX Header "Name"
81
+ Test::More \- \s-1TAP\s0 Testing for Bash
82
+ .SH "Synopsis"
83
+ .IX Header "Synopsis"
84
+ Write a test file like this. Maybe call it \f(CW\*(C`test/test.t\*(C'\fR:
85
+ .PP
86
+ .Vb 1
87
+ \& #!/usr/bin/env bash
88
+ \&
89
+ \& TEST_MORE_PATH="/path/to/test\-more\-bash"
90
+ \& BASHLIB="\`
91
+ \& find $TEST_MORE_PATH \-type d |
92
+ \& grep \-E \*(Aq/(bin|lib)$\*(Aq |
93
+ \& xargs \-n1 printf "%s:"\`"
94
+ \& PATH="$BASHLIB$PATH"
95
+ \&
96
+ \& source bash+ :std
97
+ \&
98
+ \& use Test::More
99
+ \&
100
+ \& plan tests 8
101
+ \&
102
+ \& some\-command
103
+ \& ok $? \*(Aqsome\-command is ok\*(Aq
104
+ \&
105
+ \& # or:
106
+ \& ok "\`some\-command\`" \*(Aqsome\-command is ok\*(Aq
107
+ \&
108
+ \& pass \*(AqThis will always pass\*(Aq
109
+ \&
110
+ \& fail \*(AqThis will always fail\*(Aq
111
+ \&
112
+ \& is \`echo foo\` \*(Aqfoo\*(Aq \*(Aqfoo is foo\*(Aq
113
+ \&
114
+ \& isnt foo bar "foo isn\*(Aqt bar"
115
+ \&
116
+ \& like food foo \*(Aqfood is like foo\*(Aq
117
+ \&
118
+ \& unlike team I "There\*(Aqs no \*(AqI\*(Aq in \*(Aqteam\*(Aq"
119
+ \&
120
+ \& diag "A message for stderr"
121
+ \&
122
+ \& note "A message for stdout"
123
+ .Ve
124
+ .PP
125
+ Run the test with \f(CW\*(C`prove\*(C'\fR like this:
126
+ .PP
127
+ .Vb 1
128
+ \& prove test/test.t
129
+ .Ve
130
+ .PP
131
+ Prove knows it's Bash from the first line (the hashbang), and it just works.
132
+ .SH "Description"
133
+ .IX Header "Description"
134
+ Test::More is the tried and true testing library for Perl. It uses \s-1TAP \s0(the Test Anything Protocol). This is the same thing for Bash. For the most part it should work exactly the same.
135
+ .SH "Methods"
136
+ .IX Header "Methods"
137
+ This is the basic usage:
138
+ .IP "\(bu" 4
139
+ \&\f(CW\*(C`plan tests $count\*(C'\fR
140
+ .IP "\(bu" 4
141
+ \&\f(CW\*(C`ok $status_code "$label"\*(C'\fR
142
+ .IP "\(bu" 4
143
+ \&\f(CW\*(C`pass "$label"\*(C'\fR
144
+ .IP "\(bu" 4
145
+ \&\f(CW\*(C`fail "$label"\*(C'\fR
146
+ .IP "\(bu" 4
147
+ \&\f(CW\*(C`is "$got" "$want" "label"\*(C'\fR
148
+ .IP "\(bu" 4
149
+ \&\f(CW\*(C`isnt "$got" "$unwanted" "$label"\*(C'\fR
150
+ .IP "\(bu" 4
151
+ \&\f(CW\*(C`like "$got" "$regex" "$label"\*(C'\fR
152
+ .IP "\(bu" 4
153
+ \&\f(CW\*(C`unlike "$got" "$regex" "$label"\*(C'\fR
154
+ .IP "\(bu" 4
155
+ \&\f(CW\*(C`diag "$message"\*(C'\fR
156
+ .IP "\(bu" 4
157
+ \&\f(CW\*(C`note "$message"\*(C'\fR
158
+ .IP "\(bu" 4
159
+ \&\f(CW\*(C`done_testing $count\*(C'\fR
160
+ .IP "\(bu" 4
161
+ \&\f(CW\*(C`plan skip_all "$reason"\*(C'\fR
162
+ .IP "\(bu" 4
163
+ \&\f(CW\*(C`BAIL_OUT "$reason"\*(C'\fR
164
+ .PP
165
+ More detailed info coming soon.
166
+ .SH "Author"
167
+ .IX Header "Author"
168
+ Ingy döt Net <ingy@bpan.org>
169
+ .SH "Copyright & License"
170
+ .IX Header "Copyright & License"
171
+ Copyright 2013\-2016. Ingy döt Net.
172
+ .PP
173
+ The \s-1MIT\s0 License (\s-1MIT\s0)
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env bash
2
+
3
+ source test/setup
4
+
5
+ use Test::More
6
+
7
+ output=$(prove -v test/test/fail1.t 2>&1) || true
8
+
9
+ like "$output" 'not ok 1 - fail with label' \
10
+ 'fail with label'
11
+ like "$output" 'not ok 2' \
12
+ 'fail with no label'
13
+ like "$output" 'not ok 3 - is foo bar' \
14
+ 'fail output is correct'
15
+ like "$output" "# got: 'foo'" \
16
+ 'difference reporting - got'
17
+ like "$output" "# expected: 'bar'" \
18
+ 'difference reporting - want'
19
+
20
+ done_testing 5
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env bash
2
+
3
+ source test/setup
4
+ use Test::More
5
+
6
+ plan tests 5
7
+
8
+ pass 'This test always passes'
9
+
10
+ is 'foo' "foo" 'foo is foo'
11
+
12
+ ok "`true`" 'true is true'
13
+
14
+ ok "`[ 123 -eq $((61+62)) ]`" 'Math works'
15
+
16
+ ok "`[[ ! team =~ I ]]`" "There's no I in team"
17
+
18
+ # diag "A msg for stderr"
19
+
20
+ note "A msg for stdout"
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env bash
2
+
3
+ source test/setup
4
+
5
+ use Test::More tests 3
6
+
7
+ pass 'pass 1 - with label'
8
+ pass
9
+ pass 'pass 3 - 2 has no label'
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+
3
+ set -e
4
+
5
+ BASHLIB="`find $PWD -type d | grep -E '/(bin|lib)$' | xargs -n1 printf "%s:"`"
6
+ PATH="$BASHLIB:$PATH"
7
+
8
+ source bash+ :std
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env bash
2
+
3
+ source test/setup
4
+ use Test::More
5
+
6
+ output=$(prove -v test/test/skip_all.t 2>&1) || true
7
+
8
+ like "$output" 'skipped: Skipping this test to demo skip_all' \
9
+ 'skip_all works'
10
+
11
+ done_testing 1
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env bash
2
+
3
+ source test/setup
4
+ use Test::More
5
+
6
+ fail 'fail with label'
7
+
8
+ fail
9
+
10
+ is foo bar 'is foo bar'
11
+
12
+ done_testing 3
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env bash
2
+
3
+ source test/setup
4
+ use Test::More
5
+
6
+ plan skip_all 'Skipping this test to demo skip_all'
7
+
8
+ fail "Don't run this code"
9
+
10
+ done_testing
@@ -0,0 +1,1901 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ #
4
+ # Copyright 2013-2020 - Ingy döt Net <ingy@ingy.net>
5
+ #
6
+
7
+
8
+ # Exit on any errors:
9
+ set -e
10
+
11
+ # Import Bash+ helper functions:
12
+ SOURCE="$BASH_SOURCE"
13
+ while [[ -h $SOURCE ]]; do
14
+ DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
15
+ SOURCE="$(readlink "$SOURCE")"
16
+ [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE"
17
+ done
18
+ SOURCE_DIR="$(dirname "$SOURCE")"
19
+
20
+ if [[ -z "$GIT_SUBREPO_ROOT" ]]; then
21
+ # If `make install` installation used:
22
+ source "${SOURCE_DIR}/git-subrepo.d/bash+.bash"
23
+ else
24
+ # If `source .rc` method used:
25
+ source "${SOURCE_DIR}/../ext/bashplus/lib/bash+.bash"
26
+ fi
27
+ bash+:import :std can
28
+
29
+ VERSION=0.4.1
30
+ REQUIRED_GIT_VERSION=2.7.0
31
+ GIT_TMP="$(git rev-parse --git-common-dir 2> /dev/null || echo .git)/tmp"
32
+
33
+ # `git rev-parse` turns this into a getopt parser and a command usage message:
34
+ GETOPT_SPEC="\
35
+ git subrepo <command> <arguments> <options>
36
+
37
+ Commands:
38
+ clone Clone a remote repository into a local subdirectory
39
+ init Turn a current subdirectory into a subrepo
40
+ pull Pull upstream changes to the subrepo
41
+ push Push local subrepo changes upstream
42
+
43
+ fetch Fetch a subrepo's remote branch (and create a ref for it)
44
+ branch Create a branch containing the local subrepo commits
45
+ commit Commit a merged subrepo branch into the mainline
46
+
47
+ status Get status of a subrepo (or all of them)
48
+ clean Remove branches, remotes and refs for a subrepo
49
+ config Set subrepo configuration properties
50
+
51
+ help Documentation for git-subrepo (or specific command)
52
+ version Display git-subrepo version info
53
+ upgrade Upgrade the git-subrepo software itself
54
+
55
+ See 'git help subrepo' for complete documentation and usage of each command.
56
+
57
+ Options:
58
+ --
59
+ h Show the command summary
60
+ help Help overview
61
+ version Print the git-subrepo version number
62
+
63
+ a,all Perform command on all current subrepos
64
+ A,ALL Perform command on all subrepos and subsubrepos
65
+ b,branch= Specify the upstream branch to push/pull/fetch
66
+ e,edit Edit commit message
67
+ f,force Force certain operations
68
+ F,fetch Fetch the upstream content first
69
+ M,method= Method when you join, valid options are 'merge' or 'rebase'
70
+ Default is 'merge'
71
+ m,message= Specify a commit message
72
+ r,remote= Specify the upstream remote to push/pull/fetch
73
+ s,squash Squash commits on push
74
+ u,update Add the --branch and/or --remote overrides to .gitrepo
75
+
76
+ q,quiet Show minimal output
77
+ v,verbose Show verbose output
78
+ d,debug Show the actual commands used
79
+ x,DEBUG Turn on -x Bash debugging
80
+ "
81
+
82
+ #------------------------------------------------------------------------------
83
+ # Top level function:
84
+ #------------------------------------------------------------------------------
85
+ main() {
86
+ # Define global variables:
87
+ local command= # Subrepo subcommand to run
88
+ local command_arguments=() # Command args after getopt parsing
89
+ local commit_msg_args=() # Arguments to show in the commit msg
90
+ local subrepos=() # List of multiple subrepos
91
+
92
+ local all_wanted=false # Apply command to all subrepos
93
+ local ALL_wanted=false # Apply command to all subrepos and subsubrepos
94
+ local force_wanted=false # Force certain operations
95
+ local fetch_wanted=false # Fetch requested before a command
96
+ local squash_wanted=false # Squash commits on push
97
+ local update_wanted=false # Update .gitrepo with --branch and/or --remote
98
+
99
+ local quiet_wanted=false # Output should be quiet
100
+ local verbose_wanted=false # Output should be verbose
101
+ local debug_wanted=false # Show debug messages
102
+
103
+ local subdir= # Subdirectory of the subrepo being used
104
+ local subref= # Valid git ref format of subdir
105
+ local gitrepo= # Path to .gitrepo file
106
+ local worktree= # Worktree created by 'git worktree'
107
+ local start_pwd=$(pwd) # Store the original directory
108
+
109
+ local original_head_commit= # HEAD commit id at start of command
110
+ local original_head_branch= # HEAD ref at start of command
111
+ local upstream_head_commit= # HEAD commit id from a subrepo fetch
112
+
113
+ local subrepo_remote= # Remote url for subrepo's upstream repo
114
+ local subrepo_branch= # Upstream branch to clone/push/pull
115
+ local subrepo_commit= # Upstream HEAD from previous clone/pull
116
+ local subrepo_parent= # Local commit from before previous clone/pull
117
+ local subrepo_former= # A retired gitrepo key that might still exist
118
+
119
+ local refs_subrepo_branch= # A subrepo ref -> commit of branch/pull command
120
+ local refs_subrepo_commit= # A subrepo ref -> commit last merged
121
+ local refs_subrepo_fetch= # A subrepo ref -> FETCH_HEAD after fetch
122
+ local refs_subrepo_push= # A subrepo ref -> branch after push
123
+
124
+ local override_remote= # Remote specified with -r
125
+ local override_branch= # Remote specified with -b
126
+
127
+ local edit_wanted=false # Edit commit message using -e
128
+ local wanted_commit_message= # Custom commit message using -m
129
+
130
+ local join_method= # Current join method (rebase/merge)
131
+
132
+ local FAIL=true # Flag for RUN: fail on error
133
+ local OUT=false # Flag for RUN: put output in $output
134
+ local TTY=false # Flag for RUN: print output directly
135
+ local SAY=true # Flag for RUN: print command for verbose
136
+ local EXEC=false # Flag for RUN: run subprocess
137
+ local OK=true # Flag that commands have succeeded
138
+ local CODE=0 # Failure reason code
139
+ local INDENT= # Verbose indentation
140
+
141
+ local git_version= # Git version in use
142
+
143
+ # Check environment and parse CLI options:
144
+ assert-environment-ok
145
+
146
+ # Parse and validate command options:
147
+ get-command-options "$@"
148
+
149
+ # Make sure repo is in the proper state:
150
+ assert-repo-is-ready
151
+
152
+ command-init
153
+
154
+ if $all_wanted && [[ ! $command =~ ^(help|status)$ ]]; then
155
+ # Run the command on all subrepos
156
+ local args=( "${command_arguments[@]}" )
157
+ get-all-subrepos
158
+ for subdir in ${subrepos[*]}; do
159
+ command-prepare
160
+ subrepo_remote=
161
+ subrepo_branch=
162
+ command_arguments=( "$subdir" "${args[@]}" )
163
+ "command:$command"
164
+ done
165
+ else
166
+ # Run the command on a specific subrepo
167
+ command-prepare
168
+ "command:$command"
169
+ fi
170
+ }
171
+
172
+ #------------------------------------------------------------------------------
173
+ # API command functions.
174
+ #
175
+ # Most of these commands call a subrepo:$command function to do the actual
176
+ # work. The user facing output (via `say`) is done up here. The
177
+ # subrepo:* worker functions are meant to be called internally and don't print
178
+ # info to the user.
179
+ #------------------------------------------------------------------------------
180
+
181
+ # `git subrepo clone <url> [<subdir>]` command:
182
+ command:clone() {
183
+ command-setup +subrepo_remote subdir:guess-subdir
184
+
185
+ # Clone (or reclone) the subrepo into the subdir:
186
+ local reclone_up_to_date=false
187
+ subrepo:clone
188
+ if "$reclone_up_to_date"; then
189
+ say "Subrepo '$subdir' is up to date."
190
+ return
191
+ fi
192
+
193
+ # Successful command output:
194
+ local re=
195
+ $force_wanted && re=re
196
+ local remote="$subrepo_remote"
197
+ say "Subrepo '$remote' ($subrepo_branch) ${re}cloned into '$subdir'."
198
+ }
199
+
200
+ # `git subrepo init <subdir>` command:
201
+ command:init() {
202
+ command-setup +subdir
203
+ local remote="${subrepo_remote:=none}"
204
+ local branch="${subrepo_branch:=master}"
205
+
206
+ # Init new subrepo from the subdir:
207
+ subrepo:init
208
+ if OK; then
209
+ if [[ $remote == none ]]; then
210
+ say "Subrepo created from '$subdir' (with no remote)."
211
+ else
212
+ say "Subrepo created from '$subdir' with remote '$remote' ($branch)."
213
+ fi
214
+ else
215
+ die "Unknown init error code: '$CODE'"
216
+ fi
217
+ return 0
218
+ }
219
+
220
+ # `git subrepo pull <subdir>` command:
221
+ command:pull() {
222
+ command-setup +subdir
223
+
224
+ subrepo:pull
225
+ if OK; then
226
+ say "Subrepo '$subdir' pulled from '$subrepo_remote' ($subrepo_branch)."
227
+ elif [[ $CODE -eq -1 ]]; then
228
+ say "Subrepo '$subdir' is up to date."
229
+ elif [[ $CODE -eq 1 ]]; then
230
+ error-join
231
+ return "$CODE"
232
+ else
233
+ die "Unknown pull error code: '$CODE'"
234
+ fi
235
+ return 0
236
+ }
237
+
238
+ # `git subrepo push <subdir>` command:
239
+ command:push() {
240
+ local branch=
241
+ command-setup +subdir branch
242
+
243
+ subrepo:push
244
+ if OK; then
245
+ say "Subrepo '$subdir' pushed to '$subrepo_remote' ($subrepo_branch)."
246
+ elif [[ $CODE -eq -2 ]]; then
247
+ say "Subrepo '$subdir' has no new commits to push."
248
+ elif [[ $CODE -eq 1 ]]; then
249
+ error-join
250
+ return "$CODE"
251
+ else
252
+ die "Unknown push error code: '$CODE'"
253
+ fi
254
+ return 0
255
+ }
256
+
257
+ # `git subrepo fetch <subdir>` command
258
+ command:fetch() {
259
+ command-setup +subdir
260
+ if [[ $subrepo_remote == "none" ]]; then
261
+ say "Ignored '$subdir', no remote."
262
+ else
263
+ subrepo:fetch
264
+ say "Fetched '$subdir' from '$subrepo_remote' ($subrepo_branch)."
265
+ fi
266
+ }
267
+
268
+ # `git subrepo branch <subdir>` command:
269
+ command:branch() {
270
+ command-setup +subdir
271
+ if $fetch_wanted; then
272
+ CALL subrepo:fetch
273
+ fi
274
+
275
+ local branch="subrepo/$subref"
276
+ if $force_wanted; then
277
+ # We must make sure that the worktree is removed as well
278
+ worktree="$GIT_TMP/$branch"
279
+ git:delete-branch "$branch"
280
+ fi
281
+
282
+ if git:branch-exists "$branch"; then
283
+ error "Branch '$branch' already exists. Use '--force' to override."
284
+ fi
285
+
286
+ # Create the subrepo branch:
287
+ subrepo:branch
288
+
289
+ say "Created branch '$branch' and worktree '$worktree'."
290
+ }
291
+
292
+ # `git subrepo commit <subdir>` command
293
+ command:commit() {
294
+ command-setup +subdir subrepo_commit_ref
295
+
296
+ if "$fetch_wanted"; then
297
+ CALL subrepo:fetch
298
+ fi
299
+ git:rev-exists "$refs_subrepo_fetch" ||
300
+ error "Can't find ref '$refs_subrepo_fetch'. Try using -F."
301
+ upstream_head_commit="$(git rev-parse "$refs_subrepo_fetch")"
302
+
303
+ [[ -n $subrepo_commit_ref ]] ||
304
+ subrepo_commit_ref="subrepo/$subref"
305
+ subrepo:commit
306
+
307
+ say "Subrepo commit '$subrepo_commit_ref' committed as"
308
+ say "subdir '$subdir/' to branch '$original_head_branch'."
309
+ }
310
+
311
+ # `git subrepo status [<subdir>]` command:
312
+ command:status() {
313
+ subrepo:status | ${GIT_SUBREPO_PAGER}
314
+ }
315
+
316
+ status-refs() {
317
+ local output=
318
+ while read line; do
319
+ [[ $line =~ ^([0-9a-f]+)\ refs/subrepo/$subref/([a-z]+) ]] || continue
320
+ local sha1=; sha1="$(git rev-parse --short "${BASH_REMATCH[1]}")"
321
+ local type="${BASH_REMATCH[2]}"
322
+ local ref="refs/subrepo/$subref/$type"
323
+ if [[ $type == branch ]]; then
324
+ output+=" Branch Ref: $sha1 ($ref)"$'\n'
325
+ elif [[ $type == commit ]]; then
326
+ output+=" Commit Ref: $sha1 ($ref)"$'\n'
327
+ elif [[ $type == fetch ]]; then
328
+ output+=" Fetch Ref: $sha1 ($ref)"$'\n'
329
+ elif [[ $type == pull ]]; then
330
+ output+=" Pull Ref: $sha1 ($ref)"$'\n'
331
+ elif [[ $type == push ]]; then
332
+ output+=" Push Ref: $sha1 ($ref)"$'\n'
333
+ fi
334
+ done < <(git show-ref)
335
+ if [[ -n $output ]]; then
336
+ printf " Refs:\n$output"
337
+ fi
338
+ }
339
+
340
+ # `git subrepo clean <subdir>` command
341
+ command:clean() {
342
+ command-setup +subdir
343
+ local clean_list=()
344
+ subrepo:clean
345
+ for item in "${clean_list[@]}"; do
346
+ say "Removed $item."
347
+ done
348
+ }
349
+
350
+ # Wrap git config $gitrepo
351
+ command:config() {
352
+ command-setup +subdir +config_option config_value
353
+ o "Update '$subdir' configuration with $config_option=$config_value"
354
+
355
+ if [[ ! $config_option =~ ^(branch|cmdver|commit|method|remote|version)$ ]]; then
356
+ error "Option $config_option not recognized"
357
+ fi
358
+
359
+ if [[ -z $config_value ]]; then
360
+ OUT=true RUN git config --file="$gitrepo" "subrepo.$config_option"
361
+ say "Subrepo '$subdir' option '$config_option' has value '$output'."
362
+ return
363
+ fi
364
+
365
+ if ! $force_wanted; then
366
+ # Only allow changing method without force
367
+ if [[ ! $config_option == "method" ]]; then
368
+ error "This option is autogenerated, use '--force' to override."
369
+ fi
370
+ fi
371
+
372
+ if [[ $config_option == "method" ]]; then
373
+ if [[ ! $config_value =~ ^(merge|rebase)$ ]]; then
374
+ error "Not a valid method. Valid options are 'merge' or 'rebase'."
375
+ fi
376
+ fi
377
+
378
+ RUN git config --file="$gitrepo" "subrepo.$config_option" "$config_value"
379
+ say "Subrepo '$subdir' option '$config_option' set to '$config_value'."
380
+ }
381
+
382
+
383
+ # Launch the manpage viewer:
384
+ command:help() {
385
+ source "${SOURCE_DIR}/git-subrepo.d/help-functions.bash"
386
+ local cmd="${command_arguments[0]}"
387
+ if [[ -n $cmd ]]; then
388
+ if can "help:$cmd"; then
389
+ "help:$cmd"
390
+ echo
391
+ else
392
+ err "No help found for '$cmd'"
393
+ fi
394
+ elif $all_wanted; then
395
+ help:all
396
+ else
397
+ exec git help subrepo
398
+ fi
399
+ msg_ok=0
400
+ }
401
+
402
+ # Print version info.
403
+ # TODO: Add short commit id after version.
404
+ # Will need to get it from repo or make install can put it somewhere.
405
+ command:version() {
406
+ cat <<...
407
+ git-subrepo Version: $VERSION
408
+ Copyright 2013-2020 Ingy döt Net
409
+ https://github.com/ingydotnet/git-subrepo
410
+ $BASH_SOURCE
411
+ Git Version: $git_version
412
+
413
+ ...
414
+ :
415
+ }
416
+
417
+ command:upgrade() {
418
+ local path="$0"
419
+ if [[ $path =~ ^/ && $path =~ ^(.*/git-subrepo)/lib/git-subrepo$ ]]; then
420
+ local subrepo_root="${BASH_REMATCH[1]}"
421
+ (
422
+ o "Change directory to '$subrepo_root'."
423
+ cd "${BASH_REMATCH[1]}"
424
+
425
+ local branch="$(git rev-parse --abbrev-ref HEAD)"
426
+ if [[ $branch != master ]]; then
427
+ error "git-subrepo repo is not on the 'master' branch"
428
+ fi
429
+
430
+ o "'git pull' latest version."
431
+ RUN git pull --ff-only
432
+
433
+ say "git-subrepo is up to date."
434
+ )
435
+ else
436
+ die "\
437
+
438
+ Sorry. Your installation can't use the 'git subrepo upgrade' command. The
439
+ command only works if you installed git subrepo by adding
440
+ '/path/to/git-subrepo' to your PATH.
441
+
442
+ If you used 'make install' to install git-subrepo, then just do this:
443
+
444
+ cd /path/to/git-subrepo
445
+ git pull
446
+ make install
447
+
448
+ "
449
+ fi
450
+ }
451
+
452
+ #------------------------------------------------------------------------------
453
+ # Subrepo command worker functions.
454
+ #------------------------------------------------------------------------------
455
+
456
+ # Clone by fetching remote content into our subdir:
457
+ subrepo:clone() {
458
+ re="$1"
459
+
460
+ FAIL=false RUN git rev-parse HEAD
461
+ if ! OK; then
462
+ error "You can't clone into an empty repository"
463
+ fi
464
+
465
+ # Turn off force unless really a reclone:
466
+ if $force_wanted && [[ ! -f $gitrepo ]]; then
467
+ force_wanted=false
468
+ fi
469
+
470
+ if $force_wanted; then
471
+ o "--force indicates a reclone."
472
+ CALL subrepo:fetch
473
+ read-gitrepo-file
474
+ o "Check if we already are up to date."
475
+ if [[ $upstream_head_commit == $subrepo_commit ]]; then
476
+ reclone_up_to_date=true
477
+ return
478
+ fi
479
+ o "Remove the old subdir."
480
+ RUN git rm -r -- "$subdir"
481
+ else
482
+ assert-subdir-empty
483
+ if [[ -z $subrepo_branch ]]; then
484
+ o "Determine the upstream head branch."
485
+ get-upstream-head-branch
486
+ subrepo_branch="$output"
487
+ fi
488
+
489
+ CALL subrepo:fetch
490
+ fi
491
+
492
+ o "Make the directory '$subdir/' for the clone."
493
+ RUN mkdir -p -- "$subdir"
494
+
495
+ o "Commit the new '$subdir/' content."
496
+ subrepo_commit_ref="$upstream_head_commit"
497
+ CALL subrepo:commit
498
+ }
499
+
500
+ # Init a new subrepo from current repo:
501
+ subrepo:init() {
502
+ local branch_name="subrepo/${subref:??}"
503
+ # Check if subdir is proper candidate for this init:
504
+ assert-subdir-ready-for-init
505
+
506
+ o "Put info into '$subdir/.gitrepo' file."
507
+ update-gitrepo-file
508
+
509
+ o "Add the new '$subdir/.gitrepo' file."
510
+ # -f from pull request #219. TODO needs test.
511
+ RUN git add -f -- "$gitrepo"
512
+
513
+ o "Commit new subrepo to the '$original_head_branch' branch."
514
+ subrepo_commit_ref="$original_head_commit"
515
+ RUN git commit -m "$(get-commit-message)"
516
+
517
+ o "Create ref '$refs_subrepo_commit'."
518
+ git:make-ref "$refs_subrepo_commit" "$subrepo_commit_ref"
519
+ }
520
+
521
+ # Properly merge a local subrepo branch with upstream and commit to mainline:
522
+ subrepo:pull() {
523
+ CALL subrepo:fetch
524
+
525
+ # Check if we already are up to date
526
+ # If the -u flag is present, always perform the operation
527
+ if [[ $upstream_head_commit == $subrepo_commit ]] && ! $update_wanted; then
528
+ OK=false; CODE=-1; return
529
+ fi
530
+
531
+ local branch_name="subrepo/$subref"
532
+ git:delete-branch "$branch_name"
533
+
534
+ subrepo_commit_ref="$branch_name"
535
+
536
+ o "Create subrepo branch '$branch_name'."
537
+ CALL subrepo:branch
538
+ cd "$worktree";
539
+
540
+ if [[ "$join_method" == "rebase" ]]; then
541
+ o "Rebase changes to $refs_subrepo_fetch"
542
+ FAIL=false OUT=true RUN git rebase "$refs_subrepo_fetch" "$branch_name"
543
+ if ! OK; then
544
+ say "The \"git rebase\" command failed:"
545
+ say
546
+ say " ${output//$'\n'/$'\n' }"
547
+ CODE=1
548
+ return
549
+ fi
550
+ else
551
+ o "Merge in changes from $refs_subrepo_fetch"
552
+ FAIL=false OUT=true RUN git merge "$refs_subrepo_fetch"
553
+ if ! OK; then
554
+ say "The \"git merge\" command failed:"
555
+ say
556
+ say " ${output//$'\n'/$'\n' }"
557
+ CODE=1
558
+ return
559
+ fi
560
+ fi
561
+
562
+ o "Back to $start_pwd"
563
+ cd "$start_pwd";
564
+
565
+ o "Create ref '$refs_subrepo_branch' for branch '$branch_name'."
566
+ git:make-ref "$refs_subrepo_branch" "$branch_name"
567
+
568
+ o "Commit the new '$subrepo_commit_ref' content."
569
+ CALL subrepo:commit
570
+ }
571
+
572
+ # Push a properly merged subrepo branch upstream:
573
+ subrepo:push() {
574
+ local branch_name="$branch"
575
+ local new_upstream=false
576
+ local branch_created=false
577
+
578
+ if [[ -z $branch_name ]]; then
579
+ FAIL=false OUT=false CALL subrepo:fetch
580
+
581
+ if ! OK; then
582
+ # Check if we are pushing to a new upstream repo (or branch) and just
583
+ # push the commit directly. This is common after a `git subrepo init`:
584
+ # Force to case in
585
+ local re="(^|"$'\n'")fatal: couldn't find remote ref "
586
+ if [[ ${output,,} =~ $re ]]; then
587
+ o "Pushing to new upstream: $subrepo_remote ($subrepo_branch)."
588
+ new_upstream=true
589
+ else
590
+ error "Fetch for push failed: $output"
591
+ fi
592
+ else
593
+ # Check that we are up to date:
594
+ o "Check upstream head against .gitrepo commit."
595
+ if ! $force_wanted; then
596
+ if [[ $upstream_head_commit != $subrepo_commit ]]; then
597
+ error "There are new changes upstream, you need to pull first."
598
+ fi
599
+ fi
600
+ fi
601
+
602
+ branch_name="subrepo/$subref"
603
+ git:delete-branch "$branch_name"
604
+
605
+ if $squash_wanted; then
606
+ o "Squash commits"
607
+ subrepo_parent="HEAD^"
608
+ fi
609
+
610
+ o "Create subrepo branch '$branch_name'."
611
+ CALL subrepo:branch "$branch_name"
612
+ cd "$worktree";
613
+
614
+ if [[ "$join_method" == "rebase" ]]; then
615
+ o "Rebase changes to $refs_subrepo_fetch"
616
+ FAIL=false OUT=true RUN git rebase "$refs_subrepo_fetch" "$branch_name"
617
+ if ! OK; then
618
+ say "The \"git rebase\" command failed:"
619
+ say
620
+ say " ${output//$'\n'/$'\n' }"
621
+ CODE=1
622
+ return
623
+ fi
624
+ fi
625
+ branch_created=true
626
+ cd "$start_pwd"
627
+ else
628
+ if $squash_wanted; then
629
+ error "Squash option (-s) can't be used with branch parameter"
630
+ fi
631
+ fi
632
+
633
+ o "Make sure that '$branch_name' exists."
634
+ git:branch-exists "$branch_name" ||
635
+ error "No subrepo branch '$branch_name' to push."
636
+
637
+ o "Check if we have something to push"
638
+ new_upstream_head_commit="$(git rev-parse "$branch_name")"
639
+ if ! $new_upstream; then
640
+ if [[ $upstream_head_commit == $new_upstream_head_commit ]]; then
641
+ OK=false
642
+ CODE=-2
643
+ return
644
+ fi
645
+ fi
646
+
647
+ if ! $force_wanted; then
648
+ o "Make sure '$branch_name' contains the '$refs_subrepo_fetch' HEAD."
649
+ if ! git:commit-in-rev-list "$upstream_head_commit" "$branch_name"; then
650
+ error "Can't commit: '$branch_name' doesn't contain upstream HEAD: " \
651
+ "$upstream_head_commit"
652
+ fi
653
+ fi
654
+
655
+ local force=''
656
+ "$force_wanted" && force=' --force'
657
+
658
+ o "Push$force branch '$branch_name' to '$subrepo_remote' ($subrepo_branch)."
659
+ RUN git push$force "$subrepo_remote" "$branch_name":"$subrepo_branch"
660
+
661
+ o "Create ref '$refs_subrepo_push' for branch '$branch_name'."
662
+ git:make-ref "$refs_subrepo_push" "$branch_name"
663
+
664
+ if $branch_created; then
665
+ o "Remove branch '$branch_name'."
666
+ git:delete-branch "$branch_name"
667
+ fi
668
+
669
+ o "Put updates into '$subdir/.gitrepo' file."
670
+ upstream_head_commit="$new_upstream_head_commit"
671
+ subrepo_commit_ref="$upstream_head_commit"
672
+ update-gitrepo-file
673
+ RUN git commit -m "$(get-commit-message)"
674
+ }
675
+
676
+ # Fetch the subrepo's remote branch content:
677
+ subrepo:fetch() {
678
+ if [[ $subrepo_remote == none ]]; then
679
+ error "Can't fetch subrepo. Remote is 'none' in '$subdir/.gitrepo'."
680
+ fi
681
+
682
+ o "Fetch the upstream: $subrepo_remote ($subrepo_branch)."
683
+ RUN git fetch --no-tags --quiet "$subrepo_remote" "$subrepo_branch"
684
+ OK || return
685
+
686
+ o "Get the upstream subrepo HEAD commit."
687
+ OUT=true RUN git rev-parse FETCH_HEAD^0
688
+ upstream_head_commit="$output"
689
+
690
+ o "Create ref '$refs_subrepo_fetch'."
691
+ git:make-ref "$refs_subrepo_fetch" FETCH_HEAD^0
692
+ }
693
+
694
+ # Create a subrepo branch containing all changes
695
+ subrepo:branch() {
696
+ local branch="${1:-"subrepo/$subref"}"
697
+ o "Check if the '$branch' branch already exists."
698
+ git:branch-exists "$branch" && return
699
+
700
+ local last_gitrepo_commit=
701
+ local first_gitrepo_commit=
702
+
703
+ o "Subrepo parent: $subrepo_parent"
704
+ if [[ -n "$subrepo_parent" ]]; then
705
+ local prev_commit=
706
+ local ancestor=
707
+ o "Create new commits with parents into the subrepo fetch"
708
+ OUT=true RUN git rev-list --reverse --ancestry-path --topo-order "$subrepo_parent..HEAD"
709
+ local commit_list="$output"
710
+ for commit in $commit_list; do
711
+ o "Working on $commit"
712
+
713
+ FAIL=false OUT=true RUN git config --blob \
714
+ "$commit":"$subdir/.gitrepo" "subrepo.commit"
715
+ if [[ -z "$output" ]]; then
716
+ o "Ignore commit, no .gitrepo file"
717
+ continue
718
+ fi
719
+
720
+ local gitrepo_commit="$output"
721
+ o ".gitrepo reference commit: $gitrepo_commit"
722
+
723
+
724
+ # Only include the commit if it's a child of the previous commit
725
+ # This way we create a single path between $subrepo_parent..HEAD
726
+ if [[ -n "$ancestor" ]]; then
727
+ local is_direct_child=$(git show -s --pretty=format:"%P" $commit | grep "$ancestor")
728
+ o "is child: $is_direct_child"
729
+ if [[ -z "$is_direct_child" ]]; then
730
+ o "Ignore $commit, it's not in the selected path"
731
+ continue
732
+ fi
733
+ fi
734
+
735
+ # Remember the previous commit from the parent repo path
736
+ ancestor="$commit"
737
+
738
+ o "Check for rebase"
739
+ if git:rev-exists "$refs_subrepo_fetch"; then
740
+ if ! git:commit-in-rev-list "$gitrepo_commit" "$refs_subrepo_fetch"; then
741
+ error "Local repository does not contain $gitrepo_commit. Try to 'git subrepo fetch $subref' or add the '-F' flag to always fetch the latest content."
742
+ fi
743
+ fi
744
+
745
+ o "Find parents"
746
+ local first_parent=
747
+ [[ -n $prev_commit ]] && first_parent="-p $prev_commit"
748
+ local second_parent=
749
+ if [[ -z "$first_gitrepo_commit" ]]; then
750
+ first_gitrepo_commit="$gitrepo_commit"
751
+ second_parent="-p $gitrepo_commit"
752
+ fi
753
+
754
+ if [[ "$join_method" != "rebase" ]]; then
755
+ # In the rebase case we don't create merge commits
756
+ if [[ "$gitrepo_commit" != "$last_gitrepo_commit" ]]; then
757
+ second_parent="-p $gitrepo_commit"
758
+ last_gitrepo_commit="$gitrepo_commit"
759
+ fi
760
+ fi
761
+
762
+ o "Create a new commit $first_parent $second_parent"
763
+ FAIL=false RUN git cat-file -e "$commit":"$subdir"
764
+ if OK; then
765
+ o "Create with content"
766
+ local PREVIOUS_IFS=$IFS
767
+ IFS=$'\n'
768
+ local author_info=( $(git log -1 --format=%ad%n%ae%n%an "$commit") )
769
+ local commiter_info=( $(git log -1 --format=%cd%n%ce%n%cn "$commit") )
770
+ IFS=$PREVIOUS_IFS
771
+
772
+ # When we create new commits we leave the author information unchanged
773
+ # the committer will though be updated to the current user
774
+ # This should be analog how cherrypicking is handled allowing git
775
+ # to store both the original author but also the responsible committer
776
+ # that created the local version of the commit and pushed it.
777
+ prev_commit=$(git log -n 1 --format=%B "$commit" |
778
+ GIT_AUTHOR_DATE="${author_info[0]}" \
779
+ GIT_AUTHOR_EMAIL="${author_info[1]}" \
780
+ GIT_AUTHOR_NAME="${author_info[2]}" \
781
+ GIT_COMMITTER_DATE="${commiter_info[0]}" \
782
+ GIT_COMMITTER_EMAIL="${commiter_info[1]}" \
783
+ GIT_COMMITTER_NAME="${commiter_info[2]}" \
784
+ git commit-tree -F - $first_parent $second_parent "$commit":"$subdir")
785
+ else
786
+ o "Create empty placeholder"
787
+ prev_commit=$(git commit-tree -m "EMPTY" \
788
+ $first_parent $second_parent "4b825dc642cb6eb9a060e54bf8d69288fbee4904")
789
+ fi
790
+ done
791
+
792
+ o "Create branch '$branch' for this new commit set $prev_commit."
793
+ RUN git branch "$branch" "$prev_commit"
794
+ else
795
+ o "No parent setting, use the subdir content."
796
+ RUN git branch "$branch" HEAD
797
+ TTY=true FAIL=false RUN git filter-branch -f --subdirectory-filter \
798
+ "$subref" "$branch"
799
+ fi
800
+
801
+ o "Remove the .gitrepo file from $first_gitrepo_commit..$branch"
802
+ local filter="$branch"
803
+ [[ -n "$first_gitrepo_commit" ]] && filter="$first_gitrepo_commit..$branch"
804
+ FAIL=false RUN git filter-branch -f --prune-empty --tree-filter \
805
+ "rm -f .gitrepo" "$filter"
806
+
807
+ git:create-worktree "$branch"
808
+
809
+ o "Create ref '$refs_subrepo_branch'."
810
+ git:make-ref "$refs_subrepo_branch" "$branch"
811
+ }
812
+
813
+ # Commit a merged subrepo branch:
814
+ subrepo:commit() {
815
+ o "Check that '$subrepo_commit_ref' exists."
816
+ git:rev-exists "$subrepo_commit_ref" ||
817
+ error "Commit ref '$subrepo_commit_ref' does not exist."
818
+
819
+ if ! "$force_wanted"; then
820
+ local upstream="$upstream_head_commit"
821
+ o "Make sure '$subrepo_commit_ref' contains the upstream HEAD."
822
+ if ! git:commit-in-rev-list "$upstream" "$subrepo_commit_ref"; then
823
+ error \
824
+ "Can't commit: '$subrepo_commit_ref' doesn't contain upstream HEAD."
825
+ fi
826
+ fi
827
+
828
+ if git ls-files -- "$subdir" | grep -q .; then
829
+ o "Remove old content of the subdir."
830
+ RUN git rm -r -- "$subdir"
831
+ fi
832
+
833
+ o "Put remote subrepo content into '$subdir/'."
834
+ RUN git read-tree --prefix="$subdir" -u "$subrepo_commit_ref"
835
+
836
+ o "Put info into '$subdir/.gitrepo' file."
837
+ update-gitrepo-file
838
+ RUN git add -f -- "$gitrepo"
839
+
840
+ local commit_message
841
+ if [[ -n "$wanted_commit_message" ]]; then
842
+ commit_message="$wanted_commit_message"
843
+ else
844
+ commit_message="$(get-commit-message)"
845
+ fi
846
+
847
+ local edit_flag=
848
+ $edit_wanted && edit_flag=--edit
849
+
850
+ [[ -n $commit_message ]] || commit_message="$(get-commit-message)"
851
+
852
+ local edit_flag=
853
+ $edit_wanted && edit_flag=--edit
854
+
855
+ o "Commit to the '$original_head_branch' branch."
856
+ if [[ $original_head_commit != none ]]; then
857
+ RUN git commit $edit_flag -m "$commit_message"
858
+ else
859
+ # We had cloned into an empty repo, side effect of prior git reset --mixed
860
+ # command is that subrepo's history is now part of the index. Commit
861
+ # without that history.
862
+ OUT=true RUN git write-tree
863
+ OUT=true RUN git commit-tree $edit_flag -m "$commit_message" "$output"
864
+ RUN git reset --hard "$output"
865
+ fi
866
+
867
+ # Clean up worktree to indicate that we are ready
868
+ git:remove-worktree
869
+
870
+ o "Create ref '$refs_subrepo_commit'."
871
+ git:make-ref "$refs_subrepo_commit" "$subrepo_commit_ref"
872
+ }
873
+
874
+ subrepo:status() {
875
+ if [[ ${#command_arguments[@]} -eq 0 ]]; then
876
+ get-all-subrepos
877
+ local count=${#subrepos[@]}
878
+ if ! "$quiet_wanted"; then
879
+ if [[ $count -eq 0 ]]; then
880
+ echo "No subrepos."
881
+ return
882
+ else
883
+ local s=; [[ $count -eq 1 ]] || s=s
884
+ echo "$count subrepo$s:"
885
+ echo
886
+ fi
887
+ fi
888
+ else
889
+ subrepos=("${command_arguments[@]}")
890
+ fi
891
+
892
+ for subdir in "${subrepos[@]}"; do
893
+ check-and-normalize-subdir
894
+ encode-subdir
895
+
896
+ if [[ ! -f $subdir/.gitrepo ]]; then
897
+ echo "'$subdir' is not a subrepo"
898
+ echo
899
+ continue
900
+ fi
901
+
902
+ refs_subrepo_fetch="refs/subrepo/$subref/fetch"
903
+ upstream_head_commit="$(
904
+ git rev-parse --short "$refs_subrepo_fetch" 2> /dev/null || true
905
+ )"
906
+ subrepo_remote=
907
+ subrepo_branch=
908
+
909
+ read-gitrepo-file
910
+ if $fetch_wanted; then
911
+ subrepo:fetch
912
+ fi
913
+
914
+ if $quiet_wanted; then
915
+ echo "$subdir"
916
+ continue
917
+ fi
918
+
919
+ echo "Git subrepo '$subdir':"
920
+ git:branch-exists "subrepo/$subref" &&
921
+ echo " Subrepo Branch: subrepo/$subref"
922
+ local remote="subrepo/$subref"
923
+ FAIL=false OUT=true RUN git config "remote.$remote.url"
924
+ [[ -n $output ]] &&
925
+ echo " Remote Name: subrepo/$subref"
926
+ echo " Remote URL: $subrepo_remote"
927
+ [[ -n $upstream_head_commit ]] &&
928
+ echo " Upstream Ref: $upstream_head_commit"
929
+ echo " Tracking Branch: $subrepo_branch"
930
+ [[ -z $subrepo_commit ]] ||
931
+ echo " Pulled Commit: $(git rev-parse --short $subrepo_commit)"
932
+ if [[ -n $subrepo_parent ]]; then
933
+ echo " Pull Parent: $(git rev-parse --short $subrepo_parent)"
934
+ # TODO Remove this eventually:
935
+ elif [[ -n $subrepo_former ]]; then
936
+ printf " Former Commit: $(git rev-parse --short $subrepo_former)"
937
+ echo " *** DEPRECATED ***"
938
+ fi
939
+
940
+ # Grep for directory, branch can be in detached state due to conflicts
941
+ local _worktree=$(git worktree list | grep "$GIT_TMP/subrepo/$subdir")
942
+ if [[ -n $_worktree ]]; then
943
+ echo " Worktree: $_worktree"
944
+ fi
945
+
946
+ if "$verbose_wanted"; then
947
+ status-refs
948
+ fi
949
+
950
+ echo
951
+ done
952
+ }
953
+
954
+ subrepo:clean() {
955
+ # Remove subrepo branches if exist:
956
+ local branch="subrepo/$subref"
957
+ local ref="refs/heads/$branch"
958
+ local worktree="$GIT_TMP/$branch"
959
+
960
+ o "Clean $subdir"
961
+ git:remove-worktree
962
+ if [[ -e .git/$ref ]]; then
963
+ o "Remove branch '$branch'."
964
+ RUN git update-ref -d "$ref"
965
+ clean_list+=("branch '$branch'")
966
+ fi
967
+
968
+ if "$force_wanted"; then
969
+ o "Remove all subrepo refs."
970
+ local suffix=""
971
+ if ! $all_wanted; then
972
+ suffix="$subref/"
973
+ fi
974
+ git show-ref | while read hash ref; do
975
+ if [[ "$ref" == refs/subrepo/$suffix* ]]; then
976
+ git update-ref -d "$ref"
977
+ fi
978
+ done
979
+ fi
980
+ }
981
+
982
+ #------------------------------------------------------------------------------
983
+ # Support functions:
984
+ #------------------------------------------------------------------------------
985
+
986
+
987
+ # TODO:
988
+ # Collect original options and arguments into an array for commit message
989
+ # They should be normalized and pruned
990
+
991
+ # Parse command line options:
992
+ get-command-options() {
993
+ [[ $# -eq 0 ]] && set -- --help
994
+
995
+ [[ -n $GIT_SUBREPO_QUIET ]] && quiet_wanted=true
996
+ [[ -n $GIT_SUBREPO_VERBOSE ]] && verbose_wanted=true
997
+ [[ -n $GIT_SUBREPO_DEBUG ]] && debug_wanted=true
998
+
999
+ eval "$(
1000
+ echo "$GETOPT_SPEC" |
1001
+ git rev-parse --parseopt -- "$@" ||
1002
+ echo exit $?
1003
+ )"
1004
+
1005
+ while [[ $# -gt 0 ]]; do
1006
+ local option="$1"; shift
1007
+ case "$option" in
1008
+ --) break ;;
1009
+ -a) all_wanted=true ;;
1010
+ -A) ALL_wanted=true
1011
+ all_wanted=true ;;
1012
+ -b) subrepo_branch="$1"
1013
+ override_branch="$1"
1014
+ commit_msg_args+=("--branch=$1")
1015
+ shift ;;
1016
+ -e) edit_wanted=true ;;
1017
+ -f) force_wanted=true
1018
+ commit_msg_args+=("--force") ;;
1019
+ -F) fetch_wanted=true ;;
1020
+ -m) wanted_commit_message="$1"
1021
+ shift;;
1022
+ -M) join_method="$1"
1023
+ shift;;
1024
+ -M) join_method="$1"
1025
+ shift;;
1026
+ -r) subrepo_remote="$1"
1027
+ override_remote="$1"
1028
+ commit_msg_args+=("--remote=$1")
1029
+ shift ;;
1030
+ -s) squash_wanted=true ;;
1031
+ -u) update_wanted=true
1032
+ commit_msg_args+=("--update") ;;
1033
+ -q) quiet_wanted=true ;;
1034
+ -v) verbose_wanted=true ;;
1035
+ -d) debug_wanted=true ;;
1036
+ -x) set -x ;;
1037
+ --version)
1038
+ echo "$VERSION"
1039
+ exit ;;
1040
+ *) usage-error "Unexpected option: '$option'." ;;
1041
+ esac
1042
+ done
1043
+
1044
+ # Set subrepo command:
1045
+ command="$1"; shift
1046
+
1047
+ # Make sure command exists:
1048
+ can "command:$command" ||
1049
+ usage-error "'$command' is not a command. See 'git subrepo help'."
1050
+
1051
+ command_arguments=("$@")
1052
+ if [[ ${#command_arguments} -gt 0 ]]; then
1053
+ local first="${command_arguments[0]}"
1054
+ first="${first%/}"
1055
+ command_arguments[0]="$first"
1056
+ fi
1057
+ commit_msg_args+=("${command_arguments[@]}")
1058
+
1059
+ for option in all ALL edit fetch force squash; do
1060
+ var="${option}_wanted"
1061
+ if ${!var}; then
1062
+ check_option $option
1063
+ fi
1064
+ done
1065
+
1066
+ if [[ -n $override_branch ]]; then
1067
+ check_option branch
1068
+ fi
1069
+ if [[ -n $override_remote ]]; then
1070
+ check_option remote
1071
+ fi
1072
+ if [[ -n $wanted_commit_message ]]; then
1073
+ check_option message
1074
+ fi
1075
+ if $update_wanted; then
1076
+ check_option update
1077
+ if [[ -z $subrepo_branch && -z $subrepo_remote ]]; then
1078
+ usage-error "Can't use '--update' without '--branch' or '--remote'."
1079
+ fi
1080
+ fi
1081
+ }
1082
+
1083
+ options_help='all'
1084
+ options_branch='all fetch force'
1085
+ options_clean='ALL all force'
1086
+ options_clone='branch edit force message method'
1087
+ options_config='force'
1088
+ options_commit='edit fetch force message'
1089
+ options_fetch='all branch remote'
1090
+ options_init='branch remote method'
1091
+ options_pull='all branch edit force message remote update'
1092
+ options_push='all branch force remote squash update'
1093
+ options_status='ALL all fetch'
1094
+ check_option() {
1095
+ local var="options_${command//-/_}"
1096
+ [[ ${!var} =~ $1 ]] ||
1097
+ usage-error "Invalid option '--$1' for '$command'."
1098
+ }
1099
+
1100
+ #------------------------------------------------------------------------------
1101
+ # Command argument validation:
1102
+ #------------------------------------------------------------------------------
1103
+
1104
+ command-init() {
1105
+ # Export variable to let other processes (possibly git hooks) know that they
1106
+ # are running under git-subrepo. Set to current process pid, so it can be
1107
+ # further verified if need be:
1108
+ export GIT_SUBREPO_RUNNING="$$"
1109
+ export GIT_SUBREPO_COMMAND="$command"
1110
+
1111
+ : "${GIT_SUBREPO_PAGER:=${PAGER:-less}}"
1112
+ if [[ $GIT_SUBREPO_PAGER == less ]]; then
1113
+ GIT_SUBREPO_PAGER='less -FRX'
1114
+ fi
1115
+ }
1116
+
1117
+ command-prepare() {
1118
+ local output=
1119
+ if git:rev-exists HEAD; then
1120
+ git:get-head-branch-commit
1121
+ fi
1122
+ original_head_commit="${output:-none}"
1123
+ }
1124
+
1125
+ # Do the setup steps needed by most of the subrepo subcommands:
1126
+ command-setup() {
1127
+ get-params "$@"
1128
+
1129
+ check-and-normalize-subdir
1130
+ encode-subdir
1131
+ gitrepo="$subdir/.gitrepo"
1132
+
1133
+ if ! $force_wanted; then
1134
+ o "Check for worktree with branch subrepo/$subdir"
1135
+ local _worktree=$(git worktree list | grep "\[subrepo/$subdir\]" | cut -d ' ' -f1)
1136
+ if [[ $command =~ ^(commit)$ && -z $_worktree ]]; then
1137
+ error "There is no worktree available, use the branch command first"
1138
+ elif [[ ! $command =~ ^(branch|clean|commit|push)$ && -n $_worktree ]]; then
1139
+ if [[ -e $gitrepo ]]; then
1140
+ error "There is already a worktree with branch subrepo/$subdir.
1141
+ Use the --force flag to override this check or perform a subrepo clean
1142
+ to remove the worktree."
1143
+ else
1144
+ error "There is already a worktree with branch subrepo/$subdir.
1145
+ Use the --force flag to override this check or remove the worktree with
1146
+ 1. rm -rf $_worktree
1147
+ 2. git worktree prune
1148
+ "
1149
+ fi
1150
+ fi
1151
+ fi
1152
+
1153
+ # Set refs_ variables:
1154
+ refs_subrepo_branch="refs/subrepo/$subref/branch"
1155
+ refs_subrepo_commit="refs/subrepo/$subref/commit"
1156
+ refs_subrepo_fetch="refs/subrepo/$subref/fetch"
1157
+ refs_subrepo_push="refs/subrepo/$subref/push"
1158
+
1159
+ # Read/parse the .gitrepo file (unless clone/init; doesn't exist yet)
1160
+ if [[ ! $command =~ ^(clone|init)$ ]]; then
1161
+ read-gitrepo-file
1162
+ fi
1163
+
1164
+ true
1165
+ }
1166
+
1167
+ # Parse command line args according to a simple dsl spec:
1168
+ get-params() {
1169
+ local i=0
1170
+ local num=${#command_arguments[@]}
1171
+ for arg in $@; do
1172
+ local value="${command_arguments[i]}"
1173
+ value="${value//%/%%}"
1174
+ value="${value//\\/\\\\}"
1175
+ # If arg starts with '+' then it is required
1176
+ if [[ $arg == +* ]]; then
1177
+ if [[ $i -ge $num ]]; then
1178
+ usage-error "Command '$command' requires arg '${arg#+}'."
1179
+ fi
1180
+ printf -v ${arg#+} -- "$value"
1181
+ # Look for function name after ':' to provide a default value
1182
+ else
1183
+ if [[ $i -lt $num ]]; then
1184
+ printf -v ${arg%:*} -- "$value"
1185
+ elif [[ $arg =~ : ]]; then
1186
+ "${arg#*:}"
1187
+ fi
1188
+ fi
1189
+ let i=$((i+1))
1190
+ done
1191
+
1192
+ # Check for extra arguments:
1193
+ if [[ $num -gt $i ]]; then
1194
+ set -- ${command_arguments[@]}
1195
+ for ((j = 1; j <= i; j++)); do shift; done
1196
+ error "Unknown argument(s) '$*' for '$command' command."
1197
+ fi
1198
+ }
1199
+
1200
+ check-and-normalize-subdir() {
1201
+ # Sanity check subdir:
1202
+ [[ -n $subdir ]] ||
1203
+ die "subdir not set"
1204
+ [[ $subdir =~ ^/ || $subdir =~ ^[A-Z]: ]] &&
1205
+ usage-error "The subdir '$subdir' should not be absolute path."
1206
+ subdir="${subdir#./}"
1207
+ subdir="${subdir%/}"
1208
+ [[ $subdir != *//* ]] || subdir=$(tr -s / <<< "$subdir")
1209
+ }
1210
+
1211
+ # Determine the correct subdir path to use:
1212
+ guess-subdir() {
1213
+ local dir="$subrepo_remote"
1214
+ dir="${dir%.git}"
1215
+ dir="${dir%/}"
1216
+ dir="${dir##*/}"
1217
+ [[ $dir =~ ^[-_a-zA-Z0-9]+$ ]] ||
1218
+ error "Can't determine subdir from '$subrepo_remote'."
1219
+ subdir="$dir"
1220
+ check-and-normalize-subdir
1221
+ encode-subdir
1222
+ }
1223
+
1224
+ # Encode the subdir as a valid git ref format
1225
+ #
1226
+ # Input: env $subdir
1227
+ # Output: env $subref
1228
+ #
1229
+ # For detail rules about valid git refs, see the manual of git-check-ref-format:
1230
+ # URL: https://www.kernel.org/pub/software/scm/git/docs/git-check-ref-format.html
1231
+ # Shell: git check-ref-format --help
1232
+ #
1233
+ encode-subdir() {
1234
+ subref=$subdir
1235
+ if [[ ! $subref ]] || git check-ref-format "subrepo/$subref"; then
1236
+ return
1237
+ fi
1238
+
1239
+ ## 0. escape %, ensure the subref can be (almost) decoded back to subdir
1240
+ subref=${subref//%/%25}
1241
+
1242
+ ## 1. They can include slash / for hierarchical (directory) grouping,
1243
+ ## but no slash-separated component can begin with a dot . or
1244
+ ## end with the sequence .lock.
1245
+ subref=/$subref/
1246
+ subref=${subref//\/.//%2e}
1247
+ subref=${subref//.lock\//%2elock/}
1248
+ subref=${subref#/}
1249
+ subref=${subref%/}
1250
+
1251
+ ## 2. They must contain at least one /.
1252
+ ## Note: 'subrepo/' be will prefixed, so this is always true.
1253
+ ## 3. They cannot have two consecutive dots .. anywhere.
1254
+ subref=${subref//../%2e%2e}
1255
+ subref=${subref//%2e./%2e%2e}
1256
+ subref=${subref//.%2e/%2e%2e}
1257
+
1258
+ ## 4. They cannot have ASCII control characters
1259
+ ## (i.e. bytes whose values are lower than \040, or \177 DEL), space,
1260
+ ## tilde ~, caret ^, or colon : anywhere.
1261
+ ## 5. They cannot have question-mark ?, asterisk *,
1262
+ ## or open bracket [ anywhere.
1263
+ local i
1264
+ for (( i = 1; i < 32; ++i )); do
1265
+ # skip substitute NUL char (i=0), as bash will skip NUL in env
1266
+ local x=$(printf "%02x" $i)
1267
+ subref=${subref//$(printf "%b" "\x$x")/%$x}
1268
+ done
1269
+ subref=${subref//$'\177'/%7f}
1270
+ subref=${subref// /%20}
1271
+ subref=${subref//\~/%7e}
1272
+ subref=${subref//^/%5e}
1273
+ subref=${subref//:/%3a}
1274
+ subref=${subref//\?/%3f}
1275
+ subref=${subref//\*/%2a}
1276
+ subref=${subref//\[/%5b}
1277
+ subref=${subref//$'\n'/%0a}
1278
+
1279
+ ## 6. They cannot begin or end with a slash / or contain multiple
1280
+ ## consecutive slashes.
1281
+ ## Note: This rule is not revertable.
1282
+ [[ $subref != *//* ]] || subref=$(tr -s / <<< "$subref")
1283
+
1284
+ ## 7. They cannot end with a dot ..
1285
+ case "$subref" in
1286
+ *.) subref=${subref%.}
1287
+ subref+=%2e
1288
+ ;;
1289
+ esac
1290
+
1291
+ ## 8. They cannot contain a sequence @\{.
1292
+ subref=${subref//@\{/%40\{}
1293
+
1294
+ ## 9. They cannot be the single character @.
1295
+ ## Note: 'subrepo/' be will prefixed, so this is always true.
1296
+
1297
+ ## 10. They cannot contain a \.
1298
+ subref=${subref//\\/%5c}
1299
+
1300
+ subref=$(git check-ref-format --normalize --allow-onelevel "$subref") ||
1301
+ error "Can't determine valid subref from '$subdir'."
1302
+ }
1303
+
1304
+ #------------------------------------------------------------------------------
1305
+ # State file (`.gitrepo`) functions:
1306
+ #------------------------------------------------------------------------------
1307
+
1308
+ # Set subdir and gitrepo vars:
1309
+ read-gitrepo-file() {
1310
+ gitrepo="$subdir/.gitrepo"
1311
+
1312
+ if [[ ! -f $gitrepo ]]; then
1313
+ error "No '$gitrepo' file."
1314
+ fi
1315
+
1316
+ # Read .gitrepo values:
1317
+ if [[ -z $subrepo_remote ]]; then
1318
+ SAY=false OUT=true RUN git config --file="$gitrepo" subrepo.remote
1319
+ subrepo_remote="$output"
1320
+ fi
1321
+
1322
+ if [[ -z $subrepo_branch ]]; then
1323
+ SAY=false OUT=true RUN git config --file="$gitrepo" subrepo.branch
1324
+ subrepo_branch="$output"
1325
+ fi
1326
+
1327
+ SAY=false OUT=true RUN git config --file="$gitrepo" subrepo.commit
1328
+ subrepo_commit="$output"
1329
+
1330
+ FAIL=false \
1331
+ SAY=false OUT=true RUN git config --file="$gitrepo" subrepo.parent
1332
+ subrepo_parent="$output"
1333
+
1334
+ FAIL=false \
1335
+ SAY=false OUT=true RUN git config --file="$gitrepo" subrepo.method
1336
+ if [[ $output == "rebase" ]]; then
1337
+ join_method="rebase"
1338
+ else
1339
+ # This is the default method
1340
+ join_method="merge"
1341
+ fi
1342
+
1343
+ if [[ -z $subrepo_parent ]]; then
1344
+ FAIL=false \
1345
+ SAY=false OUT=true RUN git config --file="$gitrepo" subrepo.former
1346
+ subrepo_former="$output"
1347
+ fi
1348
+ }
1349
+
1350
+
1351
+ # Update the subdir/.gitrepo state file:
1352
+ update-gitrepo-file() {
1353
+ local short_commit=
1354
+
1355
+ local newfile=false
1356
+ if [[ ! -e $gitrepo ]]; then
1357
+
1358
+ FAIL=false RUN git cat-file -e "$original_head_commit":"$gitrepo"
1359
+
1360
+ if OK; then
1361
+ o "Try to recreate gitrepo file from $original_head_commit"
1362
+ git cat-file -p "$original_head_commit":"$gitrepo" > "$gitrepo"
1363
+ else
1364
+ newfile=true
1365
+ cat <<... > "$gitrepo"
1366
+ ; DO NOT EDIT (unless you know what you are doing)
1367
+ ;
1368
+ ; This subdirectory is a git "subrepo", and this file is maintained by the
1369
+ ; git-subrepo command. See https://github.com/git-commands/git-subrepo#readme
1370
+ ;
1371
+ ...
1372
+ fi
1373
+ fi
1374
+
1375
+
1376
+ # TODO: only update remote and branch if supplied and $update_wanted
1377
+ if $newfile || [[ $update_wanted && -n $override_remote ]]; then
1378
+ RUN git config --file="$gitrepo" subrepo.remote "$subrepo_remote"
1379
+ fi
1380
+
1381
+ if $newfile || [[ $update_wanted && -n $override_branch ]]; then
1382
+ RUN git config --file="$gitrepo" subrepo.branch "$subrepo_branch"
1383
+ fi
1384
+
1385
+ RUN git config --file="$gitrepo" subrepo.commit "$upstream_head_commit"
1386
+ # Only write new parent when we are at the head of upstream
1387
+ if [[ -n $upstream_head_commit && -n $subrepo_commit_ref ]]; then
1388
+ OUT=true RUN git rev-parse "$subrepo_commit_ref"
1389
+ o "$upstream_head_commit == $output"
1390
+ if [[ $upstream_head_commit == $output ]]; then
1391
+ RUN git config --file="$gitrepo" subrepo.parent "$original_head_commit"
1392
+ fi
1393
+ fi
1394
+
1395
+ [[ -z $join_method ]] && join_method="merge"
1396
+ RUN git config --file="$gitrepo" subrepo.method "$join_method"
1397
+
1398
+ RUN git config --file="$gitrepo" subrepo.cmdver "$VERSION"
1399
+
1400
+ RUN git add -f -- "$gitrepo"
1401
+ }
1402
+
1403
+ #------------------------------------------------------------------------------
1404
+ # Enviroment checks:
1405
+ #------------------------------------------------------------------------------
1406
+
1407
+ # Check that system is ok for this command:
1408
+ assert-environment-ok() {
1409
+ type git &> /dev/null ||
1410
+ error "Can't find your 'git' command in '$PATH'."
1411
+
1412
+ git_version=$(git --version | cut -d ' ' -f3)
1413
+
1414
+ if [[ $(
1415
+ printf "$REQUIRED_GIT_VERSION\n$git_version" |
1416
+ sort -t. -k 1,1n -k 2,2n -k 3,3n |
1417
+ head -n1
1418
+ ) == "$git_version" &&
1419
+ $git_version != "$REQUIRED_GIT_VERSION"
1420
+ ]]; then
1421
+ error "Requires git version $REQUIRED_GIT_VERSION or higher; "`
1422
+ `"you have '$git_version'."
1423
+ fi
1424
+
1425
+ if [[ ${BASH_VERSINFO[0]} -lt 4 ]] ; then
1426
+ echo "The git-subrepo command requires that 'Bash 4+' is installed."
1427
+ echo "It doesn't need to be your shell, but it must be in your PATH."
1428
+ if [[ $OSTYPE == darwin* ]]; then
1429
+ echo "You appear to be on macOS."
1430
+ echo "Try: 'brew install bash'."
1431
+ echo "This will not change your user shell, it just installs 'Bash 5.x'."
1432
+ fi
1433
+ exit 1
1434
+ fi
1435
+ }
1436
+
1437
+ # Make sure git repo is ready:
1438
+ assert-repo-is-ready() {
1439
+ # Skip this for trivial info commands:
1440
+ [[ $command =~ ^(help|version|upgrade)$ ]] && return
1441
+
1442
+ # We must be inside a git repo:
1443
+ git rev-parse --git-dir &> /dev/null ||
1444
+ error "Not inside a git repository."
1445
+
1446
+ # Get the original branch and commit:
1447
+ git:get-head-branch-name
1448
+ original_head_branch="$output"
1449
+
1450
+ # If a subrepo branch is currently checked out, then note it:
1451
+ if [[ $original_head_branch =~ ^subrepo/(.*) ]]; then
1452
+ error "Can't '$command' while subrepo branch is checked out."
1453
+ fi
1454
+
1455
+ # Make sure we are on a branch:
1456
+ [[ $original_head_branch == HEAD || -z $original_head_branch ]] &&
1457
+ error "Must be on a branch to run this command."
1458
+
1459
+ # In a work-tree:
1460
+ SAY=false OUT=true RUN git rev-parse --is-inside-work-tree
1461
+ [[ $output == true ]] ||
1462
+ error "Can't 'subrepo $command' outside a working tree."
1463
+
1464
+ # HEAD exists:
1465
+ [[ $command == clone ]] ||
1466
+ RUN git rev-parse --verify HEAD
1467
+
1468
+ assert-working-copy-is-clean
1469
+
1470
+ # For now, only support actions from top of repo:
1471
+ if [[ -n "$(git rev-parse --show-prefix)" ]]; then
1472
+ error "Need to run subrepo command from top level directory of the repo."
1473
+ fi
1474
+ }
1475
+
1476
+ assert-working-copy-is-clean() {
1477
+ # Repo is in a clean state:
1478
+ if [[ $command =~ ^(clone|init|pull|push|branch|commit)$ ]]; then
1479
+ # TODO: Should we check for untracked files?
1480
+ local pwd=$(pwd)
1481
+ o "Assert that working copy is clean: $pwd"
1482
+ git update-index -q --ignore-submodules --refresh
1483
+ git diff-files --quiet --ignore-submodules ||
1484
+ error "Can't $command subrepo. Unstaged changes. ($pwd)"
1485
+ if [[ $command != clone ]] || git:rev-exists HEAD; then
1486
+ git diff-index --quiet --ignore-submodules HEAD ||
1487
+ error "Can't $command subrepo. Working tree has changes. ($pwd)"
1488
+ git diff-index --quiet --cached --ignore-submodules HEAD ||
1489
+ error "Can't $command subrepo. Index has changes. ($pwd)"
1490
+ else
1491
+ # Repo has no commits and we're cloning a subrepo. Working tree won't
1492
+ # possibly have changes as there was nothing initial to change.
1493
+ [[ -z $(git ls-files) ]] ||
1494
+ error "Can't $command subrepo. Index has changes. ($pwd)"
1495
+ fi
1496
+ fi
1497
+ }
1498
+
1499
+ # If subdir exists, make sure it is empty:
1500
+ assert-subdir-ready-for-init() {
1501
+ if [[ ! -e $subdir ]]; then
1502
+ error "The subdir '$subdir' does not exist."
1503
+ fi
1504
+ if [[ -e $subdir/.gitrepo ]]; then
1505
+ error "The subdir '$subdir' is already a subrepo."
1506
+ fi
1507
+ # Check that subdir is part of the repo
1508
+ if [[ -z $(git log -1 -- $subdir) ]]; then
1509
+ error "The subdir '$subdir' is not part of this repo."
1510
+ fi
1511
+ }
1512
+
1513
+ # If subdir exists, make sure it is empty:
1514
+ assert-subdir-empty() {
1515
+ if [[ -e $subdir ]] && [[ -n $(ls -A $subdir) ]]; then
1516
+ error "The subdir '$subdir' exists and is not empty."
1517
+ fi
1518
+ }
1519
+
1520
+ #------------------------------------------------------------------------------
1521
+ # Getters of various information:
1522
+ #------------------------------------------------------------------------------
1523
+
1524
+ # Find all the current subrepos by looking for all the subdirectories that
1525
+ # contain a `.gitrepo` file.
1526
+ get-all-subrepos() {
1527
+ local paths=($(git ls-files | sed -n 's!/\.gitrepo$!!p' | sort))
1528
+ subrepos=()
1529
+ local path
1530
+ for path in "${paths[@]}"; do
1531
+ add-subrepo "$path"
1532
+ done
1533
+ }
1534
+
1535
+ add-subrepo() {
1536
+ if ! $ALL_wanted; then
1537
+ for path in "${subrepos[@]}"; do
1538
+ [[ $1 =~ ^$path ]] && return
1539
+ done
1540
+ fi
1541
+ subrepos+=("$1")
1542
+ }
1543
+
1544
+ # Determine the upstream's default head branch:
1545
+ get-upstream-head-branch() {
1546
+ OUT=true RUN git ls-remote $subrepo_remote
1547
+ local remotes="$output"
1548
+ [[ -n $remotes ]] ||
1549
+ error "Failed to 'git ls-remote $subrepo_remote'."
1550
+ local commit="$(
1551
+ echo "$remotes" |
1552
+ grep HEAD |
1553
+ cut -f1
1554
+ )"
1555
+ local branch="$(
1556
+ echo "$remotes" |
1557
+ grep -E "$commit[[:space:]]+refs/heads/" |
1558
+ grep -v HEAD |
1559
+ head -n1 |
1560
+ cut -f2
1561
+ )"
1562
+ [[ $branch =~ refs/heads/ ]] ||
1563
+ error "Problem finding remote default head branch."
1564
+ output="${branch#refs/heads/}"
1565
+ }
1566
+
1567
+ # Commit msg for an action commit:
1568
+ # Don't use RUN here as it will pollute commit message
1569
+ get-commit-message() {
1570
+ local commit="none"
1571
+ if git:rev-exists "$upstream_head_commit"; then
1572
+ commit=$(git rev-parse --short "$upstream_head_commit")
1573
+ fi
1574
+
1575
+ local args=() debug_wanted=false
1576
+ if $all_wanted; then
1577
+ args+=("$subdir")
1578
+ fi
1579
+ args+=(${commit_msg_args[@]})
1580
+
1581
+ # Find the specific git-subrepo code used:
1582
+ local command_remote='???'
1583
+ local command_commit='???'
1584
+ get-command-info
1585
+
1586
+ local merged="none"
1587
+ if git:rev-exists "$subrepo_commit_ref"; then
1588
+ merged=$(git rev-parse --short "$subrepo_commit_ref")
1589
+ fi
1590
+
1591
+ local is_merge=""
1592
+ if [[ $command != push ]]; then
1593
+ if git:is_merge_commit "$subrepo_commit_ref"; then
1594
+ is_merge=" (merge)"
1595
+ fi
1596
+ fi
1597
+
1598
+ # TODO: Consider output for push!
1599
+
1600
+ # Format subrepo commit message:
1601
+ cat <<...
1602
+ git subrepo $command$is_merge ${args[@]}
1603
+
1604
+ subrepo:
1605
+ subdir: "$subdir"
1606
+ merged: "$merged"
1607
+ upstream:
1608
+ origin: "$subrepo_remote"
1609
+ branch: "$subrepo_branch"
1610
+ commit: "$commit"
1611
+ git-subrepo:
1612
+ version: "$VERSION"
1613
+ origin: "$command_remote"
1614
+ commit: "$command_commit"
1615
+ ...
1616
+ }
1617
+
1618
+ # Get location and version info about the git-subrepo command itself. This
1619
+ # info goes into commit messages, so we can find out exactly how the commits
1620
+ # were done.
1621
+ get-command-info() {
1622
+ local bin="$0"
1623
+ if [[ $bin =~ / ]]; then
1624
+ local lib="$(dirname "$bin")"
1625
+ # XXX Makefile needs to install these symlinks:
1626
+ # If `git-subrepo` was system-installed (`make install`):
1627
+ if [[ -e $lib/git-subrepo.d/upstream ]] &&
1628
+ [[ -e $lib/git-subrepo.d/commit ]]; then
1629
+ command_remote=$(readlink "$lib/git-subrepo.d/upstream")
1630
+ command_commit=$(readlink "$lib/git-subrepo.d/commit")
1631
+ elif [[ $lib =~ / ]]; then
1632
+ lib="$(dirname "$lib")"
1633
+ if [[ -d $lib/.git ]]; then
1634
+ local remote="$(
1635
+ GIT_DIR=$lib/.git git remote -v |
1636
+ grep '^origin' |
1637
+ head -n1 |
1638
+ cut -f2 |
1639
+ cut -d ' ' -f1
1640
+ )"
1641
+ if [[ -n $remote ]]; then
1642
+ command_remote="$remote"
1643
+ else
1644
+ local remote="$(
1645
+ GIT_DIR=$lib/.git git remote -v |
1646
+ head -n1 |
1647
+ cut -f2 |
1648
+ cut -d ' ' -f1
1649
+ )"
1650
+ if [[ -n $remote ]]; then
1651
+ command_remote="$remote"
1652
+ fi
1653
+ fi
1654
+ local commit="$(GIT_DIR="$lib/.git" git rev-parse --short HEAD)"
1655
+ if [[ -n $commit ]]; then
1656
+ command_commit="$commit"
1657
+ fi
1658
+ fi
1659
+ fi
1660
+ fi
1661
+ }
1662
+
1663
+ #------------------------------------------------------------------------------
1664
+ # Instructional errors:
1665
+ #------------------------------------------------------------------------------
1666
+
1667
+ error-join() {
1668
+ cat <<...
1669
+
1670
+ You will need to finish the $command by hand. A new working tree has been
1671
+ created at $worktree so that you can resolve the conflicts
1672
+ shown in the output above.
1673
+
1674
+ This is the common conflict resolution workflow:
1675
+
1676
+ 1. cd $worktree
1677
+ 2. Resolve the conflicts (see "git status").
1678
+ 3. "git add" the resolved files.
1679
+ ...
1680
+
1681
+ if [[ "$join_method" == "rebase" ]]; then
1682
+ cat <<...
1683
+ 4. git rebase --continue
1684
+ ...
1685
+ else
1686
+ cat <<...
1687
+ 4. git commit
1688
+ ...
1689
+ fi
1690
+
1691
+ cat <<...
1692
+ 5. If there are more conflicts, restart at step 2.
1693
+ 6. cd $start_pwd
1694
+ ...
1695
+ local branch_name="${branch:=subrepo/$subdir}"
1696
+ if [[ "$command" == "push" ]]; then
1697
+ cat <<...
1698
+ 7. git subrepo push $subdir $branch_name
1699
+ ...
1700
+ else
1701
+ cat <<...
1702
+ 7. git subrepo commit $subdir
1703
+ ...
1704
+ fi
1705
+
1706
+ if [[ "$command" == "pull" && "$join_method" == "rebase" ]]; then
1707
+ cat <<...
1708
+
1709
+ After you have performed the steps above you can push your local changes
1710
+ without repeating the rebase by:
1711
+ 1. git subrepo push $subdir $branch_name
1712
+
1713
+ ...
1714
+ fi
1715
+
1716
+ cat <<...
1717
+ See "git help $join_method" for details.
1718
+
1719
+ Alternatively, you can abort the $command and reset back to where you started:
1720
+
1721
+ 1. git subrepo clean $subdir
1722
+
1723
+ See "git help subrepo" for more help.
1724
+
1725
+ ...
1726
+ }
1727
+
1728
+ #------------------------------------------------------------------------------
1729
+ # Git command wrappers:
1730
+ #------------------------------------------------------------------------------
1731
+
1732
+ git:branch-exists() {
1733
+ git:rev-exists "refs/heads/$1"
1734
+ }
1735
+
1736
+ git:rev-exists() {
1737
+ git rev-list "$1" -1 &> /dev/null
1738
+ }
1739
+
1740
+ git:ref-exists() {
1741
+ test -n "$(git for-each-ref "$1")"
1742
+ }
1743
+
1744
+ git:get-head-branch-name() {
1745
+ output=
1746
+ local name="$(git symbolic-ref --short --quiet HEAD)"
1747
+ [[ $name == HEAD ]] && return
1748
+ output="$name"
1749
+ }
1750
+
1751
+ git:get-head-branch-commit() {
1752
+ output="$(git rev-parse HEAD)"
1753
+ }
1754
+
1755
+ git:commit-in-rev-list() {
1756
+ local commit="$1"
1757
+ local list_head="$2"
1758
+ git rev-list "$list_head" | grep -q "^$commit"
1759
+ }
1760
+
1761
+ git:make-ref() {
1762
+ local ref_name="$1"
1763
+ local commit="$(git rev-parse "$2")"
1764
+ RUN git update-ref "$ref_name" "$commit"
1765
+ }
1766
+
1767
+ git:is_merge_commit() {
1768
+ local commit="$1"
1769
+ git show --summary "$commit" | grep -q ^Merge:
1770
+ }
1771
+
1772
+ git:create-worktree() {
1773
+ local branch="$1"
1774
+ worktree="$GIT_TMP/$branch"
1775
+ RUN git worktree add "$worktree" "$branch"
1776
+ }
1777
+
1778
+ git:remove-worktree() {
1779
+ o "Remove worktree: $worktree"
1780
+ if [[ -d "$worktree" ]]; then
1781
+ o "Check worktree for unsaved changes"
1782
+ cd "$worktree"
1783
+ assert-working-copy-is-clean
1784
+ cd "$start_pwd"
1785
+
1786
+ o "Clean up worktree $worktree"
1787
+ rm -rf "$worktree"
1788
+ RUN git worktree prune
1789
+ fi
1790
+ }
1791
+
1792
+ git:delete-branch() {
1793
+ local branch="$1"
1794
+ o "Deleting old '$branch' branch."
1795
+ # Remove worktree first, otherwise you can't delete the branch
1796
+ git:remove-worktree
1797
+ FAIL=false RUN git branch -D "$branch"
1798
+ }
1799
+
1800
+
1801
+ #------------------------------------------------------------------------------
1802
+ # Low level sugar commands:
1803
+ #------------------------------------------------------------------------------
1804
+
1805
+ # Smart command runner:
1806
+ RUN() {
1807
+ $debug_wanted && $SAY && say '>>>' $*
1808
+ if $EXEC; then
1809
+ "$@"
1810
+ return $?
1811
+ fi
1812
+
1813
+ OK=true
1814
+ set +e
1815
+ local rc=
1816
+ local out=
1817
+ if $debug_wanted && $TTY && interactive; then
1818
+ "$@"
1819
+ else
1820
+ if $OUT; then
1821
+ out="$("$@" 2>/dev/null)"
1822
+ else
1823
+ out="$("$@" 2>&1)"
1824
+ fi
1825
+ fi
1826
+ rc=$?
1827
+ set -e
1828
+
1829
+ if [[ $rc -ne 0 ]]; then
1830
+ OK=false
1831
+ $FAIL && error "Command failed: '$*'.\n$out"
1832
+ fi
1833
+ output="$out"
1834
+ }
1835
+
1836
+
1837
+ interactive() {
1838
+ if [[ -t 0 && -t 1 ]]; then
1839
+ return 0
1840
+ else
1841
+ return 1
1842
+ fi
1843
+ }
1844
+
1845
+ # Call a function with indent increased:
1846
+ CALL() {
1847
+ local INDENT=" $INDENT"
1848
+ "$@" || true
1849
+ }
1850
+
1851
+ # Print verbose steps for commands with steps:
1852
+ o() {
1853
+ if $verbose_wanted; then
1854
+ echo "$INDENT* $@"
1855
+ fi
1856
+ }
1857
+
1858
+ # Print unless quiet mode:
1859
+ say() {
1860
+ $quiet_wanted || echo "$@"
1861
+ }
1862
+
1863
+ # Print to stderr:
1864
+ err() {
1865
+ echo "$@" >&2
1866
+ }
1867
+
1868
+ # Check if OK:
1869
+ OK() {
1870
+ $OK
1871
+ }
1872
+
1873
+ # Nicely report common error messages:
1874
+ usage-error() {
1875
+ local msg="git-subrepo: $1" usage=
1876
+ if [[ $GIT_SUBREPO_TEST_ERRORS != true ]]; then
1877
+ source "${SOURCE_DIR}/git-subrepo.d/help-functions.bash"
1878
+ if can "help:$command"; then
1879
+ msg=$'\n'"$msg"$'\n'"$("help:$command")"$'\n'
1880
+ fi
1881
+ fi
1882
+ echo "$msg" >&2
1883
+ exit 1
1884
+ }
1885
+
1886
+ # Nicely report common error messages:
1887
+ error() {
1888
+ local msg="git-subrepo: $1" usage=
1889
+ echo -e "$msg" >&2
1890
+ exit 1
1891
+ }
1892
+
1893
+ # Start at the end:
1894
+ [[ $BASH_SOURCE != "$0" ]] || main "$@"
1895
+
1896
+ # Local Variables:
1897
+ # tab-width: 2
1898
+ # sh-indentation: 2
1899
+ # sh-basic-offset: 2
1900
+ # End:
1901
+ # vim: set ft=sh sw=2 lisp: