topgit: version 0.19.13
[topgit/pro.git] / tg-import.sh
bloba8d70f3a5cd9eff1ecff773ce9e0ecbb284bae03
1 #!/bin/sh
2 # TopGit - A different patch queue manager
3 # Copyright (C) 2008 Petr Baudis <pasky@suse.cz>
4 # Copyright (C) 2008 Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
5 # Copyright (C) 2015,2017,2018,2021 Kyle J. McKay <mackyle@gmail.com>
6 # All rights reserved
7 # GPLv2
9 branch_prefix=t/
10 branch_prefix_set=
11 single=
12 single_set=
13 ranges=
14 rangecnt=0
15 basedep=
16 notesflag=
17 notesref=
19 USAGE="\
20 Usage: ${tgname:-tg} [...] import [-d <base-branch>] [<option>...] <range>...
21 Or: ${tgname:-tg} [...] import [-d <base-branch>] [<option>...] -s <name> <commit>
22 Options:
23 -p <prefix> prepend <prefix> to branch names (default is 't/')
24 --notes[=<ref>] import notes ref <ref> to .topmsg --- comment
25 --no-notes do not import any notes ref --- comment (default)"
27 ## Parse options
29 while [ -n "$1" ]; do
30 arg="$1"; shift
31 case "$arg" in
32 -d)
33 basedep="$1"; shift;;
34 -p)
35 branch_prefix_set=1
36 branch_prefix="$1"; shift;;
37 -s)
38 single_set=1
39 single="$1"; shift;;
40 --notes*)
41 val="${arg#*=}"
42 if [ "$val" = "--notes" ]; then
43 notesflag=1
44 notesref="refs/notes/commits"
45 elif [ -n "$val" ] && [ "${val#-}" = "$val" ]; then
46 case "$val" in
47 refs/notes/*) checknref="$val";;
48 notes/*) checknref="refs/$val";;
49 *) checknref="refs/notes/$val";;
50 esac
51 git check-ref-format "$checknref" >/dev/null 2>&1 ||
52 die "invalid --notes parameter $arg"
53 notesflag=1
54 notesref="$checknref"
55 else
56 die "invalid --notes parameter $arg"
57 fi;;
58 --no-notes)
59 notesflag=0
60 notesref=;;
61 -*)
62 printf '%s\n' "$USAGE" >&2
63 exit 1;;
65 ranges="$ranges $arg"; rangecnt=$(( $rangecnt + 1 ));;
66 esac
67 done
69 [ -z "$single_set" ] || [ -z "$branch_prefix_set" ] ||
70 die "-p does not work with single commit (-s <name> <commit>) mode"
72 if [ -z "$notesflag" ]; then
73 if notesflag="$(git config --bool --get topgit.notesimport 2>/dev/null)"; then
74 case "$notesflag" in
75 true) notesflag=1; notesref="refs/notes/commits";;
76 false) notesflag=0; notesref=;;
77 esac
78 elif
79 notesflag="$(git config --get topgit.notesimport 2>/dev/null)" &&
80 test -n "$notesflag"
81 then
82 case "$notesflag" in
83 "-"*) checknref="$notesflag";;
84 refs/notes/*) checknref="$notesflag";;
85 notes/*) checknref="refs/$notesflag";;
86 *) checknref="refs/notes/$notesflag";;
87 esac
88 git check-ref-format "$checknref" >/dev/null 2>&1 ||
89 die "invalid topgit.notesImport config setting \"$notesflag\""
90 notesflag=1
91 notesref="$checknref"
95 ensure_work_tree
96 ensure_clean_tree
97 ensure_ident_available
99 ## Perform import
101 get_commit_msg()
103 commit="$1"
104 headers=""
105 ! header="$(git config topgit.to)" || headers="$headers%nTo: $header"
106 ! header="$(git config topgit.cc)" || headers="$headers%nCc: $header"
107 ! header="$(git config topgit.bcc)" || headers="$headers%nBcc: $header"
108 git --no-pager log -1 --pretty=format:"From: %an <%ae>$headers%nSubject: [PATCH] %s%n%n%b" "$commit"
111 get_branch_name()
113 # nice sed script from git-format-patch.sh
114 commit="$1"
115 titleScript='
116 s/[^-a-z.A-Z_0-9]/-/g
117 s/\.\.\.*/\./g
118 s/\.*$//
119 s/--*/-/g
120 s/^-//
121 s/-$//
124 git --no-pager log -1 --pretty=format:"%s" "$commit" | sed -e "$titleScript"
127 origbasedep="$basedep"
128 isfirst=1
129 lasthead=
130 lastsymref=
132 process_commit()
134 commit="$1"
135 branch_name="$2"
136 info "---- Importing $commit to $branch_name"
137 lastsymref="$(git symbolic-ref --quiet HEAD)" || :
138 lasthead="$(git rev-parse --verify --quiet HEAD -- 2>/dev/null)" || :
139 nodeps=
140 if [ -n "$isfirst" ]; then
141 if [ "$basedep" = "" ] || [ "$basedep" = "HEAD" ] || [ "$basedep" = "@" ]; then
142 if [ -z "$lastsymref" ] || [ -z "$lasthead" ]; then
143 nodeps='--no-deps'
147 tg create --quiet --no-edit $nodeps "$branch_name" $basedep || die "tg create failed"
148 basedep=
149 get_commit_msg "$commit" > .topmsg
150 if [ "${notesflag:-0}" = "1" ] && [ -n "$notesref" ]; then
151 notesblob="$(git notes --ref="$notesref" list "$commit^0" 2>/dev/null)" || :
152 if [ -n "$notesblob" ]; then
153 notesdata="$(git cat-file blob "$notesblob" 2>/dev/null |
154 git stripspace 2>/dev/null)" || :
155 [ -z "$notesdata" ] || printf '\n---\n%s\n' "$notesdata" >>.topmsg
158 git add -f .topmsg .topdeps || die "git add failed"
159 if [ -n "$tgnosequester" ]; then
160 info "topgit.sequester is set to false, unadvisedly skipping sequester commit"
161 else
162 git commit -m "tg import create $branch_name" || die "git commit failed"
164 if ! git cherry-pick --no-commit "$commit"; then
165 info "The commit will also finish the import of this patch."
166 return 2
168 git -c topgit.sequester=false commit -C "$commit"
169 info "++++ Importing $commit finished"
170 isfirst=
173 if [ -n "$single" ]; then
174 process_commit $ranges "$single"
175 exit
178 handle_pick_failure()
180 # The import got stuck! Let the user fix it up.
181 info "You are in a subshell."
182 info "Please commit the cherry-pick resolution and then \`exit\`"
183 info "If you want to abort the cherry-pick,"
184 info "use \`exit 1\` to abort the tg import process at this point."
185 info "Use \`exit 2\` to skip importing this commit and continue."
186 if ! "${SHELL:-@SHELL_PATH@}" -i </dev/tty; then
187 ret=$?
188 if [ $ret -eq 2 ]; then
189 info "Ok, I will try to continue without importing this commit."
190 if [ -n "$tgnosequester" ]; then
191 git reset --hard HEAD
192 else
193 git reset --hard HEAD^
195 [ -z "$isfirst" ] || basedep="$(origbasedep)"
196 [ -z "$lasthead" ] || git update-ref --no-deref HEAD "$lasthead"
197 [ -z "$lastsymref" ] || git symbolic-ref HEAD "$lastsymref"
198 git update-ref -d "refs/$topbases/$branch_name" || :
199 git update-ref -d "refs/heads/$branch_name" || :
200 git reset --hard HEAD
201 return 0
202 else
203 info "Ok, you aborted the import operation at this point. Now, you just need"
204 info "to switch back to some sane branch using \`git$gitcdopt checkout\`."
205 exit 3
210 handle_one_commit()
212 case "$sign" in
213 '-')
214 info "Merged already: $comment"
217 if ! process_commit "$rev" "$branch_prefix$(get_branch_name "$rev")"; then
218 ret=$?
219 [ -z "$islast" ] || return $ret
220 handle_pick_failure
223 esac
226 # nice arg verification stolen from git-format-patch.sh
227 rangeidx=0
228 islast=
229 for revpair in $ranges; do
230 rangeidx=$(( $rangeidx + 1 ))
231 case "$revpair" in
232 ?*..?*)
233 rev1="${revpair%..*}"
234 rev2="${revpair##*..}"
236 ?*..)
237 rev1="${revpair%..}"
238 rev2="HEAD"
240 ..?*)
241 rev1="HEAD"
242 rev2="${revpair#..}"
244 ?*'^!')
245 rev2="${revpair%^!}"
246 cnt="$(git rev-list --no-walk --count --min-parents=1 --max-parents=1 "$rev2^0" -- 2>/dev/null)" || :
247 if [ "$cnt" = "1" ]; then
248 rev1="${revpair%^!}^"
249 else
250 die "Not a valid single-parent rev $rev2 ($revpair)"
254 die "Unknown range spec $revpair"
256 esac
257 git rev-parse --verify "$rev1^0" -- >/dev/null 2>&1 ||
258 die "Not a valid rev $rev1 ($revpair)"
259 git rev-parse --verify "$rev2^0" -- >/dev/null 2>&1 ||
260 die "Not a valid rev $rev2 ($revpair)"
261 git cherry -v "$rev1" "$rev2" | {
262 if read sign rev comment; then
263 while read next_sign next_rev next_comment; do
264 handle_one_commit
265 sign="$next_sign"
266 rev="$next_rev"
267 comment="$next_comment"
268 done
269 [ "$rangeidx" != "$rangecnt" ] || islast=1
270 handle_one_commit
273 test $? -eq 0
274 done
275 ec=$?
276 tmpdir_cleanup || :
277 git gc --auto || :
278 exit $ec