2 # TopGit - A different patch queue manager
3 # Copyright (C) 2008 Petr Baudis <pasky@suse.cz>
4 # Copyright (C) 2015,2017,2021 Kyle J. McKay <mackyle@gmail.com>
8 ## Set up all the tg machinery
10 : "${TG_INST_BINDIR:=@bindir@}"
15 .
"$TG_INST_BINDIR"/tg
23 # Don't do anything on a non-TopGit branch
24 if head_
=$
(git symbolic-ref
-q HEAD
); then
27 head_
="${head_#refs/heads/}"
28 git rev-parse
-q --verify "refs/$topbases/$head_^0" -- >/dev
/null ||
exit 0;;
37 # status 0 iff HEAD exists and has neither .topdeps nor .topmsg entries
40 _tbbtree
="$(git rev-parse --quiet --verify HEAD^{tree} --)" ||
return 1
41 _tbbls
="$(git ls-tree --full-tree "$_tbbtree" .topdeps .topmsg)" ||
return 1
46 # $1 => variable name (set to "" for 0 return otherwise message)
47 # $2 => tree to inspect
48 # $3 => file name to look for
49 # $4 => if non-empty allow a zero-length file
51 # 0: specified blob exists and meets $4 condition and eval "$1="
52 # 1: ls-tree gave no result for that file and eval "$1='some error message'"
53 # 2: file is of type other than blob and eval "$1='some error message'"
54 # 3: file is a zero length blob and $4 is empty and eval "$1='error message'"
64 _ls_line
="$(git ls-tree --long --full-tree "$_tree" "$_file")" ||
{
65 eval "$_var="'"cannot ls tree for $_file"'
69 [ -n "$_ls_line" ] ||
{
70 eval "$_var="'"$_file is missing"'
74 # check for type and size
79 # check file is of type blob (file)
80 [ "x$_type" = "xblob" ] ||
{
81 eval "$_var=\"\$_file is not a file (i.e. not a 'blob')\""
85 # check for positive size
86 [ -n "$_zerook" ] ||
[ "$_size" -gt 0 ] ||
{
87 eval "$_var="'"$_file has empty (i.e. 0) size"'
94 tree
=$
(git write-tree
) ||
95 die
"cannot write tree"
97 ed
=0 && v_check_topfile msg1
"$tree" ".topdeps" 1 || ed
=$?
98 em
=0 && v_check_topfile msg2
"$tree" ".topmsg" || em
=$?
99 [ $ed -ne 1 ] ||
[ $em -ne 1 ] ||
! is_bare_branch ||
exit 0
100 [ -z "$msg1" ] || fatal
"$msg1"
101 [ -z "$msg2" ] || fatal
"$msg2"
102 [ $ed -eq 0 ] && [ $em -eq 0 ] ||
exit 1
104 # Don't do anything more if neither .topdeps nor .topmsg is changing
108 headrev
="$(git rev-parse --quiet --verify HEAD --)" ||
:
109 tab
=" " # one tab in there
110 prefix
="[A-Z][0-9]*$tab"
111 if [ -n "$headrev" ]; then
112 headtree
="$headrev^{tree}"
114 headtree
="$(git mktree < /dev/null)"
116 while read -r status fn
; do case "$fn" in
119 case "$status" in "A"*) mode
=create
; esac
122 case "$status" in "A"*) mode
=create
; esac
126 $(git diff-index --cached --name-status "$headtree" | grep -e "^$prefix\\.topdeps\$" -e "^$prefix\\.topmsg\$")
128 [ -n "$changedeps" ] ||
[ -n "$changemsg" ] ||
exit 0
132 [ "$head_" != "$_dep" ] ||
133 die
"TopGit dependencies form a cycle: perpetrator is $_name"
138 # we only need to check newly added deps and for these if a path exists to the
142 [ -z "$tg_topmerge" ] ||
[ ! -s "$git_dir/tg-state/remote" ] ||
143 IFS
= read -r base_remote
<"$git_dir/tg-state/remote" ||
:
144 git
diff --cached --ignore-space-at-eol -- "$root_dir/.topdeps" | diff_added_lines |
145 while read newly_added
; do
146 ref_exists
"refs/heads/$newly_added" ||
147 { [ -n "$tg_topmerge" ] && auto_create_local_remote
"$newly_added"; } ||
148 die
"invalid branch as dependent: $newly_added"
150 # check for self as dep
151 [ "$head_" != "$newly_added" ] ||
152 die
"cannot have myself as dependent"
154 # deps can be non-tgish but we can't run recurse_deps() on them
155 ref_exists
"refs/$topbases/$newly_added" ||
158 # recurse_deps uses dfs but takes the .topdeps from the tree,
159 # therefore no endless loop in the cycle-check
160 no_remotes
=1 recurse_deps check_cycle_name
"$newly_added"
164 # check for repetitions of deps
165 depdir
="$(get_temp tg-depdir -d)" ||
166 die
"cannot check for multiple occurrences of dependents"
167 git cat-file blob
":0:.topdeps" 2>/dev
/null |
168 while read -r dep ||
[ -n "$dep" ]; do
169 [ ! -d "$depdir/$dep" ] ||
170 die
"multiple occurrences of the same dependent: $dep"
171 mkdir
-p "$depdir/$dep" ||
172 die
"cannot check for multiple occurrences of dependents"
177 # Only check .topdeps if it's been changed otherwise the assumption is it's been checked
178 [ -z "$changedeps" ] || check_topdeps
180 # If we are not sequestering TopGit files or the commit is changing only TopGit files we're done
181 [ -z "$tgnosequester" ] ||
exit 0
182 [ $
(( ${changedeps:-0} + ${changemsg:-0} )) -ne $
(git diff-index
--cached --name-only "$headtree" |
wc -l) ] ||
exit 0
184 # Sequester the TopGit-specific file changes into their own commit and notify the user we did so
185 tg_index
="$git_dir/tg-index"
186 if [ -n "$headrev" ]; then
187 GIT_INDEX_FILE
="$tg_index" git read-tree
"$headrev^{tree}"
189 GIT_INDEX_FILE
="$tg_index" git read-tree
--empty
191 prefix
="100[0-9][0-9][0-9] $octet20$hexch* 0$tab"
193 printf '%s\n' "0 $nullsha$tab.topdeps"
194 printf '%s\n' "0 $nullsha$tab.topmsg"
195 git ls-files
--cached -s --full-name |
grep -e "^$prefix\\.topdeps\$" -e "^$prefix\\.topmsg\$"
196 } | GIT_INDEX_FILE
="$tg_index" git update-index
--index-info
197 newtree
="$(GIT_INDEX_FILE="$tg_index" git write-tree)"
201 if [ -n "$changedeps" ] && [ -n "$changemsg" ]; then
202 upd
=":/.topdeps :/.topmsg"
203 files
=".topdeps and .topmsg"
204 elif [ -n "$changedeps" ]; then
211 newcommit
="$(git commit-tree -m "tg
: $mode $files" ${headrev:+-p} ${headrev:+"$headrev"} "$newtree")"
212 git update-ref
-m "tg: sequester $files changes into their own preliminary commit" HEAD
"$newcommit"
213 { unset GIT_PREFIX
; } >/dev
/null
2>&1 ||
:
214 GIT_INDEX_FILE
="$git_dir/index" && export GIT_INDEX_FILE
215 [ ! -e "$GIT_INDEX_FILE.lock" ] ||
rm -f "$GIT_INDEX_FILE.lock" >/dev
/null
2>&1 ||
:
216 [ -z "$upd" ] ||
eval "git reset $newtree -- $upd" >/dev
/null
2>&1 ||
:
217 warn
"sequestered $files changes into their own preliminary commit"
218 info
"run the same \`git commit\` command again to commit the remaining changes" >&2