]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blob - tools/libxfs-apply
xfs: remote attribute blocks aren't really userdata
[thirdparty/xfsprogs-dev.git] / tools / libxfs-apply
1 #!/bin/bash
2
3 # 2 args:
4 # libxfs-apply <repo> <commit ID or patchfile>
5
6 usage()
7 {
8 echo $*
9 echo
10 echo "Usage:"
11 echo " libxfs-apply [--verbose] --source <repodir> --commit <commit_id>"
12 echo " libxfs-apply --patch <patchfile>"
13 echo
14 echo "libxfs-apply should be run in the destination git repository."
15 exit
16 }
17
18 cleanup()
19 {
20 rm -f $PATCH
21 }
22
23 # output to stderr so it is not caught by file redirects
24 fail()
25 {
26 >&2 echo "Fail:"
27 >&2 echo $*
28 cleanup
29 exit
30 }
31
32 # filterdiff 0.3.4 is the first version that handles git diff metadata (almost)
33 # correctly. It just doesn't work properly in prior versions, so those versions
34 # can't be used to extract the commit message prior to the diff. Hence just
35 # abort and tell the user to upgrade if an old version is detected. We need to
36 # check against x.y.z version numbers here.
37 _version=`filterdiff --version | cut -d " " -f 5`
38 _major=`echo $_version | cut -d "." -f 1`
39 _minor=`echo $_version | cut -d "." -f 2`
40 _patch=`echo $_version | cut -d "." -f 3`
41 if [ $_major -eq 0 ]; then
42 if [ $_minor -lt 3 ]; then
43 fail "filterdiff $_version found. 0.3.4 or greater is required."
44 fi
45 if [ $_minor -eq 3 -a $_patch -le 3 ]; then
46 fail "filterdiff $_version found. 0.3.4 or greater is required."
47 fi
48 fi
49
50 # We should see repository contents we recognise, both at the source and
51 # destination. Kernel repositorys will have fs/xfs/libxfs, and xfsprogs
52 # repositories will have libxcmd.
53 SOURCE="kernel"
54 check_repo()
55 {
56 if [ ! -d "fs/xfs/libxfs" -a ! -d "libxcmd" ]; then
57 usage "$1 repository contents not recognised!"
58 fi
59 if [ -d "$REPO/libxcmd" ]; then
60 SOURCE="xfsprogs"
61 fi
62 }
63
64 REPO=
65 PATCH=
66 COMMIT_ID=
67 VERBOSE=
68 GUILT=0
69
70 while [ $# -gt 0 ]; do
71 case "$1" in
72 --source) REPO=$2 ; shift ;;
73 --patch) PATCH=$2; shift ;;
74 --commit) COMMIT_ID=$2 ; shift ;;
75 --verbose) VERBOSE=true ;;
76 *) usage ;;
77 esac
78 shift
79 done
80
81 if [ -n "$PATCH" ]; then
82 if [ -n "$REPO" -o -n "$COMMIT_ID" ]; then
83 usage "Need to specify either patch or source repo/commit"
84 fi
85 VERBOSE=true
86 elif [ -z "$REPO" -o -z "$COMMIT_ID" ]; then
87 usage "Need to specify both source repo and commit id"
88 fi
89
90 check_repo Destination
91
92 # Are we using guilt? This works even if no patch is applied.
93 guilt top &> /dev/null
94 if [ $? -eq 0 ]; then
95 GUILT=1
96 fi
97
98 #this is pulled from the guilt code to handle commit ids sanely.
99 # usage: munge_hash_range <hash range>
100 #
101 # this means:
102 # <hash> - one commit
103 # <hash>.. - hash until head (excludes hash, includes head)
104 # ..<hash> - until hash (includes hash)
105 # <hash1>..<hash2> - from hash to hash (inclusive)
106 #
107 # The output of this function is suitable to be passed to "git rev-list"
108 munge_hash_range()
109 {
110 case "$1" in
111 *..*..*|*\ *)
112 # double .. or space is illegal
113 return 1;;
114 ..*)
115 # e.g., "..v0.10"
116 echo ${1#..};;
117 *..)
118 # e.g., "v0.19.."
119 echo ${1%..}..HEAD;;
120 *..*)
121 # e.g., "v0.19-rc1..v0.19"
122 echo ${1%%..*}..${1#*..};;
123 ?*)
124 # e.g., "v0.19"
125 echo $1^..$1;;
126 *) # empty
127 return 1;;
128 esac
129 return 0
130 }
131
132 # Filter the patch into the right format & files for the other tree
133 filter_kernel_patch()
134 {
135 local _patch=$1
136 local _libxfs_files=""
137
138 # The files we will try to apply to
139 _libxfs_files=`mktemp`
140 ls -1 fs/xfs/libxfs/*.[ch] | sed -e "s%.*/\(.*\)%*\1%" > $_libxfs_files
141
142 # Create the new patch
143 # filterdiff will have screwed up files that source/sink /dev/null.
144 # fix that up with some sed magic.
145 filterdiff \
146 --verbose \
147 -I $_libxfs_files \
148 --strip=1 \
149 --addoldprefix=a/fs/xfs/ \
150 --addnewprefix=b/fs/xfs/ \
151 $_patch | \
152 sed -e 's, [ab]\/fs\/xfs\/\(\/dev\/null\), \1,' \
153 -e '/^diff --git/d'
154
155
156 rm -f $_libxfs_files
157 }
158
159 filter_xfsprogs_patch()
160 {
161 local _patch=$1
162 local _libxfs_files=""
163
164 # The files we will try to apply to. We need to pull this from the
165 # patch, as the may be libxfs files added in this patch and so we
166 # need to capture them.
167 _libxfs_files=`mktemp`
168 #ls -1 libxfs/*.[ch] | sed -e "s%.*/\(.*\)%*libxfs/\1%" > $_libxfs_files
169 lsdiff $_patch | sed -e "s%.*/\(.*\)%*libxfs/\1%" > $_libxfs_files
170
171 # Create the new patch
172 # filterdiff will have screwed up files that source/sink /dev/null.
173 # fix that up with some sed magic.
174 filterdiff \
175 --verbose \
176 -I $_libxfs_files \
177 --strip=3 \
178 --addoldprefix=a/ \
179 --addnewprefix=b/ \
180 $_patch | \
181 sed -e 's, [ab]\/\(\/dev\/null\), \1,' \
182 -e '/^diff --git/d'
183
184 rm -f $_libxfs_files
185 }
186
187 fixup_header_format()
188 {
189 local _source=$1
190 local _patch=$2
191 local _hdr=`mktemp`
192 local _diff=`mktemp`
193 local _new_hdr=$_hdr.new
194
195 # there's a bug in filterdiff that leaves a line at the end of the
196 # header in the filtered git show output like:
197 #
198 # difflibxfs/xfs_alloc.c b/fs/xfs/libxfs/xfs_alloc.c
199 #
200 # split the header on that (convenient!)
201 sed -e /^difflib/q $_patch > $_hdr
202 cat $_patch | awk '
203 BEGIN { difflib_seen = 0; index_seen = 0 }
204 /^difflib/ { difflib_seen++; next }
205 /^index/ { if (++index_seen == 1) { next } }
206 // { if (difflib_seen) { print $0 } }' > $_diff
207
208 # the header now has the format:
209 # commit 0d5a75e9e23ee39cd0d8a167393dcedb4f0f47b2
210 # Author: Eric Sandeen <sandeen@sandeen.net>
211 # Date: Wed Jun 1 17:38:15 2016 +1000
212 #
213 # xfs: make several functions static
214 #....
215 # Signed-off-by: Dave Chinner <david@fromorbit.com>
216 #
217 #difflibxfs/xfs_alloc.c b/fs/xfs/libxfs/xfs_alloc.c
218 #
219 # We want to format it like a normal patch with a line to say what repo
220 # and commit it was sourced from:
221 #
222 # xfs: make several functions static
223 #
224 # From: Eric Sandeen <sandeen@sandeen.net>
225 #
226 # Source kernel commit: 0d5a75e9e23ee39cd0d8a167393dcedb4f0f47b2
227 #
228 # <body>
229 #
230 # To do this, use sed to first strip whitespace, then pass it into awk
231 # to rearrange the headers.
232 sed -e 's/^ *//' $_hdr | awk -v src=$_source '
233 BEGIN {
234 date_seen=0
235 subject_seen=0
236 }
237 /^commit/ {
238 commit=$2
239 next;
240 }
241 /^Author:/ {
242 split($0, a, ":")
243 from=a[2]
244 next;
245 }
246 /^Date:/ { date_seen=1; next }
247 /^difflib/ { next }
248
249 // {
250 if (date_seen == 0)
251 next;
252 if (subject_seen == 0) {
253 if (length($0) != 0) {
254 subject_seen=1
255 subject=$0;
256 }
257 next;
258 }
259 if (subject_seen == 1) {
260 print subject
261 print
262 print "From:" from
263 print
264 print "Source " src " commit: " commit
265 subject_seen=2
266 }
267 print $0
268 }' > $_hdr.new
269
270 # now output the new patch
271 cat $_hdr.new $_diff
272
273 rm -f $_hdr* $_diff
274
275 }
276
277 apply_patch()
278 {
279 local _patch=$1
280 local _patch_name=$2
281 local _current_commit=$3
282 local _new_patch=`mktemp`
283 local _source="kernel"
284
285 # filter just the libxfs parts of the patch
286 if [ $SOURCE == "xfsprogs" ]; then
287
288 [ -n "$VERBOSE" ] || lsdiff $_patch | grep -q "[ab]/libxfs/"
289 if [ $? -ne 0 ]; then
290 echo "Doesn't look like an xfsprogs patch with libxfs changes"
291 echo "Skipping commit $_current_commit"
292 return
293 fi
294
295 filter_kernel_patch $_patch > $_new_patch
296 _source="xfsprogs"
297 elif [ $SOURCE == "kernel" ]; then
298
299 [ -n "$VERBOSE" ] || lsdiff $_patch | grep -q "[ab]/fs/xfs/libxfs/"
300 if [ $? -ne 0 ]; then
301 echo "Doesn't look like a kernel patch with libxfs changes"
302 echo "Skipping commit $_current_commit"
303 return
304 fi
305
306 filter_xfsprogs_patch $_patch > $_new_patch
307 else
308 fail "Unknown source repo type: $SOURCE"
309 fi
310
311 # now munge the header to be in the correct format.
312 fixup_header_format $_source $_new_patch > $_new_patch.2
313
314 if [ -n "$VERBOSE" ]; then
315 echo "Filtered patch from $REPO contains:"
316 lsdiff $_new_patch.2
317 fi
318
319 # Ok, now apply with guilt or patch; either may fail and require a force
320 # and/or a manual reject fixup
321 if [ $GUILT -eq 1 ]; then
322 [ -n "$VERBOSE" ] || echo "$REPO looks like a guilt directory."
323 PATCHES=`guilt applied | wc -l`
324 if [ -n "$VERBOSE" -a $PATCHES -gt 0 ]; then
325 echo -n "Top patch is: "
326 guilt top
327 fi
328
329 guilt import -P $_patch_name $_new_patch.2
330 guilt push
331 if [ $? -eq 0 ]; then
332 guilt refresh
333 else
334 echo "Guilt push failed!"
335 read -r -p "Skip of Fail [s|F]? " response
336 if [ -z "$response" -o "$response" != "s" ]; then
337 echo "Force push patch, fix and refresh."
338 echo "Restart from commit $_current_commit"
339 fail "Manual cleanup required!"
340 else
341 echo "Skipping. Manual series file cleanup needed!"
342 fi
343 fi
344 else
345 echo "Applying with patch utility:"
346 patch -p1 < $_new_patch.2
347 echo "Patch was applied in $REPO; check for rejects, etc"
348 fi
349
350 rm -f $_new_patch*
351 }
352
353 # name a guilt patch. Code is lifted from guilt import-commit.
354 name_patch()
355 {
356 s=`git log --no-decorate --pretty=oneline -1 $1 | cut -c 42-`
357
358 # Try to convert the first line of the commit message to a
359 # valid patch name.
360 fname=`printf %s "$s" | \
361 sed -e "s/&/and/g" -e "s/[ :]/_/g" -e "s,[/\\],-,g" \
362 -e "s/['\\[{}]//g" -e 's/]//g' -e 's/\*/-/g' \
363 -e 's/\?/-/g' -e 's/\.\.\.*/./g' -e 's/^\.//' \
364 -e 's/\.patch$//' -e 's/\.$//' | tr A-Z a-z`
365
366 # Try harder to make it a legal commit name by
367 # removing all but a few safe characters.
368 fname=`echo $fname|tr -d -c _a-zA-Z0-9---/\\n`
369
370 echo $fname
371 }
372
373 # single patch is easy.
374 if [ -z "$COMMIT_ID" ]; then
375 apply_patch $PATCH
376 cleanup
377 exit 0
378 fi
379
380 # switch to source repo and get individual commit IDs
381 #
382 # git rev-list gives us a list in reverse chronological order, so we need to
383 # reverse that to give us the order we require.
384 pushd $REPO > /dev/null
385 check_repo Source
386 hashr=`munge_hash_range $COMMIT_ID`
387 if [ $SOURCE == "kernel" ]; then
388 hashr="$hashr -- fs/xfs/libxfs"
389 else
390 hashr="$hashr -- libxfs"
391 fi
392
393 # grab and echo the list of commits for confirmation
394 echo "Commits to apply:"
395 commit_list=`git rev-list $hashr | tac`
396 git log --oneline $hashr |tac
397 read -r -p "Proceed [y|N]? " response
398 if [ -z "$response" -o "$response" != "y" ]; then
399 fail "Aborted!"
400 fi
401 popd > /dev/null
402
403 PATCH=`mktemp`
404 for commit in $commit_list; do
405
406 # switch to source repo and pull commit into a patch file
407 pushd $REPO > /dev/null
408 git show $commit > $PATCH || usage "Bad source commit ID!"
409 patch_name=`name_patch $commit`
410 popd > /dev/null
411
412 apply_patch $PATCH $patch_name $commit
413 done
414
415
416 cleanup