This file is indexed.

/usr/bin/git-debcherry is in gitpkg 0.27.

This file is owned by root:root, with mode 0o755.

The actual contents of the file can be viewed below.

  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
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
#!/bin/bash

# git debcherry upstream [head]

function _time()  {
    if [ "$debug" = 'yes' ]; then
	echo "# time=" $(date "+%s.%N") " line=" $1 >&2
    fi
}

function usage() {
    printf "usage: $0 [options] upstream [head]\n"
    printf "\t -d | --debug\t\tprint debugging/profiling info\n"
    printf "\t -s | --stat\t\toutput patch statistics to stdout\n"
    printf "\t (-o | --output-directory) <dir> \toutput patches to <dir>\n"
}

function patch_id() {
    if [ -z "$(git diff-tree $1)" ]; then
	echo "empty"
    else
	local ident=$(git show $1 | git patch-id)
	ident=${ident% *}
	echo $ident
    fi
}

function debug (){
    if [ "$debug" = 'yes' ]; then
	printf "$@" >&2
    fi
}
function _cleanup (){
    if [ "$debug" = 'no' ]; then
	rm -rf "$tmpdir"
    fi
}

function _checkout (){
    local ref=$1
    git checkout -f $ref >$log 2>&1 || _die "checkout failed"
}

function test_apply (){
    local hash=$1
    local ref=$2
    local want_changes=$3
    local ret=0;

    debug "test_apply $hash $ref $want_changes\n"
    old_head=$(git rev-parse HEAD)

    _checkout $ref

    if ! git cherry-pick --no-commit $hash 1>/dev/null 2>&1 ; then
	debug "cherry-pick failed in test_apply\n"
	ret=1
    fi

    git diff --cached --quiet
    local has_changes=$?

    if [ "$has_changes" != "$want_changes" ]; then
	ret=1
    fi

    debug "test_apply %d %d %d\n"  $has_changes $want_changes $ret

    # wipe out any changes
    git reset --hard HEAD >$log 2>&1 || _die "reset failed"

    _checkout $old_head

    return $ret
}

# make a temporary branch.
# usage tmpbranch prefix treeish
function tmpbranch() {
    local git_dir=${GIT_DIR-$(pwd)/$(git rev-parse --git-dir)}
    local filtered=$(mktemp $git_dir/refs/heads/$1_XXXXXXXXXX)

# git update-ref is too fussy here.
    echo $(git rev-parse $2) > $filtered
    filtered=${filtered##$git_dir/}
    echo $filtered
}

function _die() {
    echo $1 >&2
    [ -s $log ] && cat $log
    exit 1
}

tmpdir=$(mktemp --tmpdir -d git-debcherry.XXXXXX)

log=$tmpdir/log
touch $log

stat_only="no"
patch_dir=""
debug="no"
while : ; do
    case $1 in
	-h | --help)
	    usage
	    exit 0
	    ;;
	-d | --debug)
	    debug="yes"
	    shift
	    ;;
	-s | --stat)
	    stat_only="yes"
	    shift
	    ;;
	-o | --output-directory)
	    patch_dir=${2%/}
	    case $patch_dir in
		/*)
		    ;;
		*)
		    patch_dir=$(pwd)/$patch_dir
	    esac
	    shift;
	    shift;
	    ;;
	--)
	    shift
	    break
	    ;;
	-*)
	    echo "Unknown option $1" >&2
	    usage
	    exit 1
	    ;;
	*)
	break
	;;
    esac
done

if [ $# -lt 1 ]; then
    usage
    exit 1
fi


_time $LINENO

upstream=$1
if ! upstream_sha1=$(git rev-parse "$upstream" 2>$log); then
    _die "bad or missing ref: $upstream"
fi

head=${2-$(git symbolic-ref HEAD)}
head=${head##refs/heads/}
if ! head_sha1=$(git rev-parse "$head" 2>$log); then
    _die "bad or missing ref: $head"
fi

if [ "$stat_only" = "no" -a -n "$patch_dir" -a -e "$patch_dir" ]; then
    _die "$patch_dir exists, not overwriting"
fi

declare -A unmerged

orig_git_dir=${GIT_DIR-$(pwd)/$(git rev-parse --git-dir)}

git clone "$orig_git_dir" "$tmpdir"/clone 1>$log || _die "clone failed"
export GIT_WORK_TREE=$tmpdir/clone
export GIT_DIR=$tmpdir/clone/.git

# note that origin here is $orig_git_dir, so the following does
# not do any network access
if git fetch origin refs/notes/commits:refs/notes/commits >/dev/null 2>&1; then
    debug "git notes found in the repository\n"
    NOTES=true
else
    debug "no git notes found in the repository\n"
    NOTES=false
fi

tmp_upstream=$(tmpbranch upstream $upstream_sha1)
tmp_head=$(tmpbranch head $head_sha1)

_checkout $tmp_head

trap '_cleanup' EXIT

# Remove any traces of .pc (from quilt) and ./debian on temporary
# branches. This avoids conflicts when dpkg-source tries to apply
# quilt patches. --prune-empty means we are only dealing with commits
# that do somehow touch upstream.

_time $LINENO

{
# git-filter-branch doesn't really understand GIT_WORK_TREE
# git_commit_non_empty_tree provided by git-filter-branch
cd $GIT_WORK_TREE
if ! git filter-branch -f --index-filter \
	'git rm --ignore-unmatch --cached -r .pc debian' \
	--commit-filter '\
	 NEW=$(git_commit_non_empty_tree "$@"); \
	 if $NOTES && [ -n "$NEW" ] && \
	    git notes show "$GIT_COMMIT" >/dev/null 2>&1 && \
	  ! git notes show "$NEW" >/dev/null 2>&1; then \
	    git notes copy "$GIT_COMMIT" "$NEW"; \
	 fi; echo "$NEW"' \
	"$tmp_upstream".."$tmp_head" 1>$log 2>&1 ; then
    _die "filtering failed"
fi
}

_time $LINENO

# for every commit reachable from head, but not from
# upstream compute its patch-id (essentially sha1 of diff)
# and save a map back to the commit.

while read -r hash ; do
    ident=$(patch_id $hash)
    unmerged[$ident]=$hash
done < <(git rev-list --no-merges  $tmp_upstream..$tmp_head )

_time $LINENO

# now delete any found upstream; note that this only gets exact
# matches, so partial application is not caught here

while read -r ident commit ; do
    unset unmerged[$ident]
done < <(git log --patch --no-merges $tmp_upstream| git patch-id)

_time $LINENO

printf 'debcherry fixup patch\n\n' >> $tmpdir/message

debug "Starting test reverts at %s\n"  $(git rev-parse HEAD)

initial_upstream=$(git rev-parse $tmp_upstream)

while read -r hash; do
    ident=$(patch_id $hash)
    if [ -n "${unmerged[$ident]}" ]; then
	if [ -z "$(git format-patch --no-binary --stdout -1 $hash | lsdiff)" ]; then
	    shorthash=$(git rev-parse --short $hash)
	    message=$(git log --pretty=format:"%s" -1 $hash)
	    printf "skipping commit  $shorthash <$ident>; empty or binary only.\n   $message\n\n" >&2
	    continue
	fi

	# the patch should cherry pick against head, but produce no
	# changes

	if ! test_apply $hash HEAD 0 ; then
	    git --no-pager log --oneline -1 $hash >> $tmpdir/message
	    printf "\t - extra changes or conflicts\n" >> $tmpdir/message
	    continue
	fi

	# the patch should apply to upstream and produce some changes.
	# XXX: note that this is a bit heuristic (i.e. wrong). It should
	# really check against something like upstream with all of the
	# patches so far applied.

	if ! test_apply $hash $initial_upstream 1 ; then
	    git --no-pager log --oneline -1 $hash >> $tmpdir/message
	    printf "\t - no changes against upstream or conflicts\n" >> $tmpdir/message
	    continue
	fi

	if git revert  --no-edit $hash 1>/dev/null 2>&1; then
	    echo "$hash" >> $tmpdir/patch-list
	else
	    git revert --abort
	    git --no-pager log --oneline -1 $hash >> $tmpdir/message
	    printf "\t - conflict" >> $tmpdir/message
	fi
    fi
done < <(git rev-list --no-merges --topo-order $tmp_upstream..$tmp_head)

# this is where we want to build our patch series
SAVED_HEAD=$(git rev-parse HEAD)

_checkout $(git rev-parse "$tmp_upstream")

# group file deletions
DELETED_FILES=$(git  diff --diff-filter=D --name-only $tmp_upstream $tmp_head);
if [ -n "$DELETED_FILES" ]; then
    git rm $DELETED_FILES
    git commit -m'File deletions'
fi

# HEAD is now like upstream, but with files deleted

base=$(git rev-parse HEAD)

git diff $base $SAVED_HEAD | filterdiff -x '[ab]/debian/*' --clean >> $tmpdir/diff

if [ -s $tmpdir/diff ]; then
    git apply --whitespace=nowarn --index $tmpdir/diff || \
	    _die "fatal: apply failed"
    git commit -F $tmpdir/message 1>/dev/null 2>&1 || \
	_die "fatal: fixup commit failed"
fi

if [ -s $tmpdir/patch-list ]; then
    while read -r hash ; do
	debug "trying %s\n" $hash

	if ! git cherry-pick --no-edit $hash 1>/dev/null 2>&1; then
	    if [ -z "$(git diff --cached)" ]; then
		echo "skipping $hash; empty cherry-pick" >&2
		continue
	    fi
	    _die "cherry-pick $hash failed"
	fi
	if $NOTES && git notes show $hash >/dev/null 2>&1; then
		git notes copy $hash HEAD;
	fi
	if [ -z "$(git diff $base)" ]; then
	    base=$(git rev-parse HEAD);
	    debug "new base ${base}"
	fi

    done < <(tac $tmpdir/patch-list)
fi

if [ $stat_only = "yes" ]; then
    git log --reverse --oneline --stat "$base"..HEAD
else
    if [ -n "$patch_dir" ]; then
	mkdir -p "$patch_dir" || _die "mkdir failed";
	echo "# exported from git by git-debcherry" > "$patch_dir/series"
	if PATCHES=$(git format-patch --no-signature --notes -o "$patch_dir" "$base"..HEAD ); then
	    if [ -n "$PATCHES" ]; then
		echo "$PATCHES" | sed -e "s,$patch_dir/,,g" -e 's, ,\n,g' >> "$patch_dir/series"
	    else
		echo "Warning: no patches exported"
	    fi
	else
	    _die "git format-patch failed"
	fi
    else
	git format-patch --no-signature --notes --stdout $base..HEAD
    fi
fi

_time $LINENO