-
Notifications
You must be signed in to change notification settings - Fork 1
/
git
executable file
·223 lines (197 loc) · 7.62 KB
/
git
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
#!/bin/bash
# Prefix for echo
PRE="[git-wrapper]"
GIT_OPTS=("-C" "-c" "--help" "--version" "--exec-path" "--html-path" "--man-path"
"--info-path" "-p" "--paginate" "--no-pager" "--no-replace-objects"
"--bare" "--git-dir" "--work-tree" "--namespace")
# Commands we want to wrap
WRAP_CMDS=("commit" "checkout" "pull")
IGNORE_CMDS=("add" "config" "get-tar-commit-id" "merge-ours" "remote" "show-ref"
"add--interactive" "count-objects" "grep" "merge-recursive" "remote-ext" "stage"
"am" "credential" "hash-object" "merge-resolve" "remote-fd" "stash"
"annotate" "credential-cache" "help" "merge-subtree" "remote-ftp" "status"
"apply" "credential-cache--daemon" "http-backend" "merge-tree" "remote-ftps" "stripspace"
"archive" "credential-store" "http-fetch" "mergetool" "remote-http" "submodule"
"bisect" "daemon" "http-push" "mktag" "remote-https" "submodule--helper"
"bisect--helper" "describe" "imap-send" "mktree" "remote-testsvn" "subtree"
"blame" "diff" "index-pack" "mv" "repack" "symbolic-ref"
"branch" "diff-files" "init" "name-rev" "replace" "tag"
"bundle" "diff-index" "init-db" "notes" "request-pull" "unpack-file"
"cat-file" "diff-tree" "instaweb" "pack-objects" "rerere" "unpack-objects"
"check-attr" "difftool" "interpret-trailers" "pack-redundant" "reset" "update-index"
"check-ignore" "difftool--helper" "log" "pack-refs" "rev-list" "update-ref"
"check-mailmap" "fast-export" "ls-files" "patch-id" "rev-parse" "update-server-info"
"check-ref-format" "fast-import" "ls-remote" "prune" "revert" "upload-archive"
"fetch" "ls-tree" "prune-packed" "rm" "upload-pack"
"checkout-index" "fetch-pack" "mailinfo" "send-email" "var"
"cherry" "filter-branch" "mailsplit" "push" "send-pack" "verify-commit"
"cherry-pick" "fmt-merge-msg" "merge" "quiltimport" "sh-i18n--envsubst" "verify-pack"
"clean" "for-each-ref" "merge-base" "read-tree" "shell" "verify-tag"
"clone" "format-patch" "merge-file" "rebase" "shortlog" "web--browse"
"column" "fsck" "merge-index" "receive-pack" "show" "whatchanged"
"fsck-objects" "merge-octopus" "reflog" "show-branch" "worktree"
"commit-tree" "gc" "merge-one-file" "relink" "show-index" "write-tree")
# Extract the command from a git command line
function get_command {
for opt in "${GIT_OPTS[@]}"; do
if [ "$opt" != "${1%=*}" ]; then
continue
fi
# Extra shift for 2 argument options
if [ "-C" == "$1" ] || [ "-c" == "$1" ]; then
shift
fi
shift
if [ "$1" == "" ]; then
return
fi
break
done
for cmd in "${WRAP_CMDS[@]}"; do
if [ "$cmd" == "$1" ]; then
echo "$1"
return
fi
done
for cmd in "${IGNORE_CMDS[@]}"; do
if [ "$cmd" == "$1" ]; then
return
fi
done
# If not found, look up command in git alias
local alias="$("$GIT" config -l | grep "alias."$1"=")"
if [ "$alias" != "" ]; then
local command="${alias#*=}"
get_command "$command"
fi
}
function pre_command {
case "$COMMAND" in
commit) pre_commit "$@" ;;
esac
}
function pre_commit {
if [ -n "${GIT_WRAPPER_NOPRECOMMIT}" ]; then
return
fi
local branch_name="$("$GIT" symbolic-ref HEAD 2>/dev/null)"
if [ -z "$branch_name" ]; then
echo ""$PRE" ERROR: HEAD detached. You have to switch to a branch before you can commit."
echo ""$PRE" (use \"git branch <branch-name>\")"
echo ""$PRE" Disable this check by setting GIT_WRAPPER_NOPRECOMMIT to a nonempty value."
exit 1
fi
}
function post_command {
case $COMMAND in
commit) post_commit "$@";;
pull) post_checkout "$@";;
merge) post_checkout "$@";;
checkout) post_checkout "$@";;
esac
}
function post_checkout {
if [ -n "${GIT_WRAPPER_NOPOSTCHECKOUT}" ]; then
return
fi
local diff="$("$GIT" submodule status | grep ^\+)"
if [ "$diff" != "" ]; then
echo
echo ""$PRE" WARNING: One or more submodules do not match the SHA-1 found in the index"
echo ""$PRE" (use \"git submodule update\" to checkout the submodules)"
echo ""$PRE" Disable this check by setting GIT_WRAPPER_NOPOSTCHECKOUT to a nonempty value."
return
fi
}
function post_commit {
if [ -n "${GIT_WRAPPER_NOPOSTCOMMIT}" ]; then
return
fi
local git_dir="$("$GIT" rev-parse --git-dir 2>/dev/null)"
if [ -z "$git_dir" ]; then
echo ""$PRE" ERROR: \"git rev-parse --git-dir\" failed!"
exit
fi
local submodule_name="$(basename "$git_dir")"
if [ "$submodule_name" == ".git" ]; then
return
fi
local parent_top="${git_dir%.git*}"
local parent_name="$(basename "$parent_top")"
local parent_branch="$("$GIT" -C "$parent_top" symbolic-ref --short HEAD 2>/dev/null)"
echo
echo ""$PRE" You just commited to the submodule: '"$submodule_name"'"
echo ""$PRE" Do you wish to update the parent repo to this commit?"
echo ""$PRE" name: "$parent_name""
echo ""$PRE" branch: "$parent_branch""
echo ""$PRE" (set GIT_WRAPPER_NOPOSTCOMMIT to a nonempty value to never ask again)"
while true; do
read -p ""$PRE" This will require to push your changes first. [y/n] " yn
case $yn in
y ) push_submodule
commit_parent "$parent_top" "$submodule_name" "$git_dir"
push_parent "$parent_top"
break;;
n ) return;;
* ) echo "Please answer y or n.";;
esac
done
}
function push_submodule {
echo
echo ""$PRE" pushing submodule..."
local submodule_branch info
submodule_branch="$("$GIT" symbolic-ref --short HEAD 2>/dev/null)"
echo ""$PS4"git push origin "$submodule_branch""
set +e
info="$("$GIT" push origin "$submodule_branch" 2>&1)"
set -e
echo "$info"
if [ "${info#*error}" != "$info" ]; then
echo ""$PRE" ERROR: pushing submodule failed!"
echo ""$PRE" Try to manually push your changes."
exit 1
fi
}
function commit_parent {
echo
echo ""$PRE" commiting parent..."
local parent_top commit_message submodule_top
parent_top="$1"
commit_message="@"$2" "$("$GIT" log --pretty=format:'%s' | head -n1)""
submodule_top="${3#*modules/}"
echo ""$PS4"git -C "$parent_top" commit -m '"$commit_message"' -- "$submodule_top""
"$GIT" -C "$parent_top" commit -m "$commit_message" -- "$submodule_top"
}
function push_parent {
echo
echo ""$PRE" pushing parent..."
local parent_branch parent_top info
parent_top="$1"
parent_branch="$("$GIT" -C "$parent_top" symbolic-ref --short HEAD 2>/dev/null)"
echo ""$PS4"git -C "$parent_top" push origin "$parent_branch""
info="$("$GIT" -C "$parent_top" push origin "$parent_branch" 2>&1)"
echo "$info"
if [ "${info#*error}" != "$info" ]; then
echo ""$PRE" ERROR: pushing parent failed!"
echo ""$PRE" Try to manually push your changes."
exit 1
fi
}
# Assuming this script is called 'git', find the next available 'git' command
# via 'which'. Override this if you want to hard-code a different path.
GIT="$(which -a git | awk 'NR==2 {print}')"
if [ "$GIT" = "" ]; then
echo "git executable not found"
exit 1
fi
#GIT=/usr/bin/git
# TODO Remove this global variable
COMMAND="$(get_command "$@")"
pre_command "$@"
"$GIT" "$@"
rc="$?"
if [ "$rc" != "0" ]; then
exit "$rc"
fi
post_command "$@"