eac_git 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/eac_git/executables.rb +24 -4
- data/lib/eac_git/rspec.rb +17 -0
- data/lib/eac_git/version.rb +1 -1
- data/vendor/git-subrepo/Changes +110 -0
- data/vendor/git-subrepo/Intro.pod +509 -0
- data/vendor/git-subrepo/License +21 -0
- data/vendor/git-subrepo/Makefile +82 -0
- data/vendor/git-subrepo/Meta +28 -0
- data/vendor/git-subrepo/ReadMe.pod +698 -0
- data/vendor/git-subrepo/doc/comparison.swim +35 -0
- data/vendor/git-subrepo/doc/git-subrepo.swim +608 -0
- data/vendor/git-subrepo/doc/intro-to-subrepo.swim +387 -0
- data/vendor/git-subrepo/ext/bashplus/Changes +15 -0
- data/vendor/git-subrepo/ext/bashplus/License +21 -0
- data/vendor/git-subrepo/ext/bashplus/Makefile +45 -0
- data/vendor/git-subrepo/ext/bashplus/Meta +28 -0
- data/vendor/git-subrepo/ext/bashplus/ReadMe.pod +77 -0
- data/vendor/git-subrepo/ext/bashplus/bin/bash+ +43 -0
- data/vendor/git-subrepo/ext/bashplus/doc/bash+.swim +61 -0
- data/vendor/git-subrepo/ext/bashplus/lib/bash+.bash +92 -0
- data/vendor/git-subrepo/ext/bashplus/man/man1/bash+.1 +134 -0
- data/vendor/git-subrepo/ext/bashplus/man/man3/bash+.3 +134 -0
- data/vendor/git-subrepo/ext/bashplus/test/base.t +12 -0
- data/vendor/git-subrepo/ext/bashplus/test/fcopy.t +22 -0
- data/vendor/git-subrepo/ext/bashplus/test/lib/foo/bar.bash +3 -0
- data/vendor/git-subrepo/ext/bashplus/test/lib/foo/foo.bash +3 -0
- data/vendor/git-subrepo/ext/bashplus/test/source-bash+-std.t +18 -0
- data/vendor/git-subrepo/ext/bashplus/test/source-bash+.t +23 -0
- data/vendor/git-subrepo/ext/bashplus/test/test.bash +70 -0
- data/vendor/git-subrepo/ext/bashplus/test/use.t +19 -0
- data/vendor/git-subrepo/ext/test-more-bash/Changes +15 -0
- data/vendor/git-subrepo/ext/test-more-bash/License +21 -0
- data/vendor/git-subrepo/ext/test-more-bash/Makefile +20 -0
- data/vendor/git-subrepo/ext/test-more-bash/Meta +30 -0
- data/vendor/git-subrepo/ext/test-more-bash/ReadMe.pod +115 -0
- data/vendor/git-subrepo/ext/test-more-bash/doc/test-more.swim +89 -0
- data/vendor/git-subrepo/ext/test-more-bash/ext/bashplus/Changes +15 -0
- data/vendor/git-subrepo/ext/test-more-bash/ext/bashplus/License +21 -0
- data/vendor/git-subrepo/ext/test-more-bash/ext/bashplus/Makefile +45 -0
- data/vendor/git-subrepo/ext/test-more-bash/ext/bashplus/Meta +28 -0
- data/vendor/git-subrepo/ext/test-more-bash/ext/bashplus/ReadMe.pod +77 -0
- data/vendor/git-subrepo/ext/test-more-bash/ext/bashplus/bin/bash+ +43 -0
- data/vendor/git-subrepo/ext/test-more-bash/ext/bashplus/doc/bash+.swim +61 -0
- data/vendor/git-subrepo/ext/test-more-bash/ext/bashplus/lib/bash+.bash +92 -0
- data/vendor/git-subrepo/ext/test-more-bash/ext/bashplus/man/man1/bash+.1 +134 -0
- data/vendor/git-subrepo/ext/test-more-bash/ext/bashplus/man/man3/bash+.3 +134 -0
- data/vendor/git-subrepo/ext/test-more-bash/ext/bashplus/test/base.t +12 -0
- data/vendor/git-subrepo/ext/test-more-bash/ext/bashplus/test/fcopy.t +22 -0
- data/vendor/git-subrepo/ext/test-more-bash/ext/bashplus/test/lib/foo/bar.bash +3 -0
- data/vendor/git-subrepo/ext/test-more-bash/ext/bashplus/test/lib/foo/foo.bash +3 -0
- data/vendor/git-subrepo/ext/test-more-bash/ext/bashplus/test/source-bash+-std.t +18 -0
- data/vendor/git-subrepo/ext/test-more-bash/ext/bashplus/test/source-bash+.t +23 -0
- data/vendor/git-subrepo/ext/test-more-bash/ext/bashplus/test/test.bash +70 -0
- data/vendor/git-subrepo/ext/test-more-bash/ext/bashplus/test/use.t +19 -0
- data/vendor/git-subrepo/ext/test-more-bash/ext/test-tap-bash/Changes +15 -0
- data/vendor/git-subrepo/ext/test-more-bash/ext/test-tap-bash/License +21 -0
- data/vendor/git-subrepo/ext/test-more-bash/ext/test-tap-bash/Makefile +37 -0
- data/vendor/git-subrepo/ext/test-more-bash/ext/test-tap-bash/Meta +28 -0
- data/vendor/git-subrepo/ext/test-more-bash/ext/test-tap-bash/ReadMe.pod +66 -0
- data/vendor/git-subrepo/ext/test-more-bash/ext/test-tap-bash/doc/test-tap.swim +48 -0
- data/vendor/git-subrepo/ext/test-more-bash/ext/test-tap-bash/lib/test/tap.bash +153 -0
- data/vendor/git-subrepo/ext/test-more-bash/ext/test-tap-bash/man/man3/test-tap.3 +119 -0
- data/vendor/git-subrepo/ext/test-more-bash/ext/test-tap-bash/test/bail_out.t +13 -0
- data/vendor/git-subrepo/ext/test-more-bash/ext/test-tap-bash/test/done.t +10 -0
- data/vendor/git-subrepo/ext/test-more-bash/ext/test-tap-bash/test/fail.t +20 -0
- data/vendor/git-subrepo/ext/test-more-bash/ext/test-tap-bash/test/fail_fast.t +15 -0
- data/vendor/git-subrepo/ext/test-more-bash/ext/test-tap-bash/test/helper.bash +9 -0
- data/vendor/git-subrepo/ext/test-more-bash/ext/test-tap-bash/test/pass.t +9 -0
- data/vendor/git-subrepo/ext/test-more-bash/ext/test-tap-bash/test/plan.t +10 -0
- data/vendor/git-subrepo/ext/test-more-bash/ext/test-tap-bash/test/skip_all.t +20 -0
- data/vendor/git-subrepo/ext/test-more-bash/ext/test-tap-bash/test/tap.t +13 -0
- data/vendor/git-subrepo/ext/test-more-bash/ext/test-tap-bash/test/test/bail.t +14 -0
- data/vendor/git-subrepo/ext/test-more-bash/ext/test-tap-bash/test/test/fail.t +7 -0
- data/vendor/git-subrepo/ext/test-more-bash/ext/test-tap-bash/test/test/fail_fast.t +12 -0
- data/vendor/git-subrepo/ext/test-more-bash/ext/test-tap-bash/test/test/skip-all-init.t +8 -0
- data/vendor/git-subrepo/ext/test-more-bash/ext/test-tap-bash/test/test/skip-all-plan.t +9 -0
- data/vendor/git-subrepo/ext/test-more-bash/lib/test/more.bash +95 -0
- data/vendor/git-subrepo/ext/test-more-bash/man/man3/test-more.3 +173 -0
- data/vendor/git-subrepo/ext/test-more-bash/test/fail.t +20 -0
- data/vendor/git-subrepo/ext/test-more-bash/test/more.t +20 -0
- data/vendor/git-subrepo/ext/test-more-bash/test/pass.t +9 -0
- data/vendor/git-subrepo/ext/test-more-bash/test/setup +8 -0
- data/vendor/git-subrepo/ext/test-more-bash/test/skip_all.t +11 -0
- data/vendor/git-subrepo/ext/test-more-bash/test/test/fail1.t +12 -0
- data/vendor/git-subrepo/ext/test-more-bash/test/test/skip_all.t +10 -0
- data/vendor/git-subrepo/lib/git-subrepo +1901 -0
- data/vendor/git-subrepo/lib/git-subrepo.d/bash+.bash +1 -0
- data/vendor/git-subrepo/lib/git-subrepo.d/help-functions.bash +339 -0
- data/vendor/git-subrepo/man/man1/git-subrepo.1 +743 -0
- data/vendor/git-subrepo/note/0.4.0 +12 -0
- data/vendor/git-subrepo/note/AllGitCmds +148 -0
- data/vendor/git-subrepo/note/Cases +32 -0
- data/vendor/git-subrepo/note/Commands +33 -0
- data/vendor/git-subrepo/note/Dags +199 -0
- data/vendor/git-subrepo/note/Gists +7 -0
- data/vendor/git-subrepo/note/Links +25 -0
- data/vendor/git-subrepo/note/Plugins +10 -0
- data/vendor/git-subrepo/note/Spec +39 -0
- data/vendor/git-subrepo/note/Story1 +57 -0
- data/vendor/git-subrepo/note/ToDo +55 -0
- data/vendor/git-subrepo/note/design.swim +137 -0
- data/vendor/git-subrepo/note/design2.swim +85 -0
- data/vendor/git-subrepo/note/init-test +38 -0
- data/vendor/git-subrepo/note/pull-dance.txt +18 -0
- data/vendor/git-subrepo/note/recreate-rebase-conflict.sh +56 -0
- data/vendor/git-subrepo/note/subtree-rebase-fail-example/test.bash +29 -0
- data/vendor/git-subrepo/note/test-subrepo-push.sh +69 -0
- data/vendor/git-subrepo/note/test.sh +58 -0
- data/vendor/git-subrepo/pkg/bin/generate-completion.pl +210 -0
- data/vendor/git-subrepo/pkg/bin/generate-help-functions.pl +89 -0
- data/vendor/git-subrepo/share/completion.bash +42 -0
- data/vendor/git-subrepo/share/enable-completion.sh +50 -0
- data/vendor/git-subrepo/share/git-completion.bash +2738 -0
- data/vendor/git-subrepo/share/zsh-completion/_git-subrepo +81 -0
- data/vendor/git-subrepo/test/branch-all.t +41 -0
- data/vendor/git-subrepo/test/branch-rev-list-one-path.t +43 -0
- data/vendor/git-subrepo/test/branch-rev-list.t +47 -0
- data/vendor/git-subrepo/test/branch.t +52 -0
- data/vendor/git-subrepo/test/clean.t +43 -0
- data/vendor/git-subrepo/test/clone-annotated-tag.t +45 -0
- data/vendor/git-subrepo/test/clone.t +107 -0
- data/vendor/git-subrepo/test/compile.t +19 -0
- data/vendor/git-subrepo/test/config.t +58 -0
- data/vendor/git-subrepo/test/encode.t +91 -0
- data/vendor/git-subrepo/test/error.t +171 -0
- data/vendor/git-subrepo/test/fetch.t +43 -0
- data/vendor/git-subrepo/test/gitignore.t +61 -0
- data/vendor/git-subrepo/test/init.t +64 -0
- data/vendor/git-subrepo/test/issue29.t +98 -0
- data/vendor/git-subrepo/test/issue95.t +98 -0
- data/vendor/git-subrepo/test/issue96.t +96 -0
- data/vendor/git-subrepo/test/pull-all.t +38 -0
- data/vendor/git-subrepo/test/pull-merge.t +113 -0
- data/vendor/git-subrepo/test/pull-message.t +88 -0
- data/vendor/git-subrepo/test/pull-new-branch.t +58 -0
- data/vendor/git-subrepo/test/pull-ours.t +90 -0
- data/vendor/git-subrepo/test/pull-theirs.t +82 -0
- data/vendor/git-subrepo/test/pull-twice.t +44 -0
- data/vendor/git-subrepo/test/pull-worktree.t +40 -0
- data/vendor/git-subrepo/test/pull.t +99 -0
- data/vendor/git-subrepo/test/push-after-init.t +51 -0
- data/vendor/git-subrepo/test/push-force.t +56 -0
- data/vendor/git-subrepo/test/push-new-branch.t +61 -0
- data/vendor/git-subrepo/test/push-no-changes.t +29 -0
- data/vendor/git-subrepo/test/push-squash.t +56 -0
- data/vendor/git-subrepo/test/push.t +176 -0
- data/vendor/git-subrepo/test/reclone.t +45 -0
- data/vendor/git-subrepo/test/repo/bar/HEAD +1 -0
- data/vendor/git-subrepo/test/repo/bar/config +4 -0
- data/vendor/git-subrepo/test/repo/bar/objects/1f/0c4b264caed0126814a0ede851a1e0b4e16ae6 +0 -0
- data/vendor/git-subrepo/test/repo/bar/objects/87/46903fdb1b9c2101377880125917c2e05b4d69 +0 -0
- data/vendor/git-subrepo/test/repo/bar/objects/94/c86ffc745232d89f78c6f895e11e71272518db +0 -0
- data/vendor/git-subrepo/test/repo/bar/objects/c6/76c57b6576743fa56278527aa60ebd2e202a7c +0 -0
- data/vendor/git-subrepo/test/repo/bar/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 +0 -0
- data/vendor/git-subrepo/test/repo/bar/objects/f6/2a8ff3feadf39b0a98f1a86ec6d1eb33858ee9 +0 -0
- data/vendor/git-subrepo/test/repo/bar/refs/heads/master +1 -0
- data/vendor/git-subrepo/test/repo/bar/refs/tags/A +1 -0
- data/vendor/git-subrepo/test/repo/foo/HEAD +1 -0
- data/vendor/git-subrepo/test/repo/foo/config +4 -0
- data/vendor/git-subrepo/test/repo/foo/objects/a0/f4cdaaf533a936296cdebbed8206c3b9ededa8 +0 -0
- data/vendor/git-subrepo/test/repo/foo/objects/e2/1291a1ad392a9d4c51dd9586804f1467b28afd +0 -0
- data/vendor/git-subrepo/test/repo/foo/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 +0 -0
- data/vendor/git-subrepo/test/repo/foo/refs/heads/master +1 -0
- data/vendor/git-subrepo/test/repo/init/HEAD +1 -0
- data/vendor/git-subrepo/test/repo/init/config +5 -0
- data/vendor/git-subrepo/test/repo/init/objects/11/523f5dcf03b4c89b592dc8a3d0308f68da2386 +0 -0
- data/vendor/git-subrepo/test/repo/init/objects/14/2addf8ec5f37334e837440122c62f2c68a29ad +0 -0
- data/vendor/git-subrepo/test/repo/init/objects/32/5180321750a21cd7a4e7ecda319e557a4f6a09 +2 -0
- data/vendor/git-subrepo/test/repo/init/objects/3d/918c6901c02f43af5d31779dd5e1f9166aeb36 +0 -0
- data/vendor/git-subrepo/test/repo/init/objects/3e/4cb596066dce63ba4d047abddb677389b65e19 +0 -0
- data/vendor/git-subrepo/test/repo/init/objects/4b/6e53022e7a04f07887697e4f3d7c377fd9822b +0 -0
- data/vendor/git-subrepo/test/repo/init/objects/58/931fc1bd559b59c41ea738fc7ad04f9ad01bd3 +0 -0
- data/vendor/git-subrepo/test/repo/init/objects/5e/c0c28e1b806f25efdca18fcf7a74b49c3755bd +0 -0
- data/vendor/git-subrepo/test/repo/init/objects/75/fa6584e748f57eff06eebdc55e9ac21d4fcbf2 +1 -0
- data/vendor/git-subrepo/test/repo/init/objects/80/2d5edbd5e1cb7fca82b5bd38e7c8a0a496fb20 +0 -0
- data/vendor/git-subrepo/test/repo/init/objects/94/7b3d714c38791e95ad6f928b48c98bb8708acd +0 -0
- data/vendor/git-subrepo/test/repo/init/objects/95/e1f2df3f4d5f3d7a60588c25a7ca8a913d3c2a +1 -0
- data/vendor/git-subrepo/test/repo/init/objects/b1/5f4a7666baf40d949548ead946a3370e273479 +0 -0
- data/vendor/git-subrepo/test/repo/init/objects/c3/ee8978c4c5d84c3b7d00ba8e5906933d027882 +0 -0
- data/vendor/git-subrepo/test/repo/init/objects/c8/b0bffbc405ef3fad7354ff833fbec36d67ddfa +3 -0
- data/vendor/git-subrepo/test/repo/init/objects/dd/8bdb934ec848137f011fe423b185505c343626 +2 -0
- data/vendor/git-subrepo/test/repo/init/objects/e2/9be58c767cfeb27235c995d293a7d71aac0135 +2 -0
- data/vendor/git-subrepo/test/repo/init/objects/ee/1224401fc6aac595145fa727dcf6706ac8aec1 +1 -0
- data/vendor/git-subrepo/test/repo/init/objects/f1/cc1a657b2e805c400f5dcaaa76bd29c6178b1b +0 -0
- data/vendor/git-subrepo/test/repo/init/refs/heads/master +1 -0
- data/vendor/git-subrepo/test/setup +205 -0
- data/vendor/git-subrepo/test/status.t +68 -0
- data/vendor/git-subrepo/test/submodule.t +45 -0
- 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,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,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:
|