blob: dbda330082a363e726ca78d0a6992161fddf1ec2 (
plain) (
blame)
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
|
# Git subcommand loaded into the depot direnv via //tools/depot-deps that can
# display the r/number for (a) given commit(s) in depot. The r/number is a
# monotonically increasing number assigned to each commit which correspond to
# refs/r/* as created by `//ops/pipelines/static-pipeline.yaml`. They can also
# be used as TVL shortlinks and are supported by //web/atward.
{ pkgs, lib, ... }:
pkgs.writeTextFile {
name = "git-r";
destination = "/bin/git-r";
executable = true;
text = ''
set -euo pipefail
PROG_NAME="$0"
CANON_BRANCH="canon"
CANON_REMOTE="$(git config "branch.$CANON_BRANCH.remote" || echo "origin")"
CANON_HEAD="$CANON_REMOTE/$CANON_BRANCH"
usage() {
cat <<EOF
Usage: git r [-h | --usage] [<git commit> ...]
Display the r/number for the given git commit(s). If none is given,
HEAD is used as a default. The r/number is a monotonically increasing
number assigned to each commit on the $CANON_BRANCH branch in depot
equivalent to the revcount ignoring merged in branches (using
git-rev-list(1) internally).
The r/numbers displayed by \`git r\` correspond to refs created by CI
in depot, so they can be used as monotonically increasing commit
identifiers that can be used instead of a commit hash. To have
\`refs/r/*\` available locally (which is not necessary for the operation
of \`git r\`), you may have to enable fetching them like this:
git config --add remote.origin.fetch '+refs/r/*:refs/r/*'
They are created the next time you run `git fetch origin`.
EOF
exit "''${1:-0}"
}
eprintf() {
printf "$@" 1>&2
}
revs=()
if [[ $# -le 0 ]]; then
revs+=("HEAD")
fi
for arg in "$@"; do
# No flags supported at the moment
case "$arg" in
# --help is mapped to `man git-r` by git(1)
# TODO(sterni): git-r man page
-h | --usage)
usage
;;
-*)
eprintf 'error: unknown flag %s\n' "$PROG_NAME" "$arg"
usage 100 1>&2
;;
*)
revs+=("$arg")
;;
esac
done
for rev in "''${revs[@]}"; do
# Make sure $rev is well formed
git rev-parse "$rev" -- > /dev/null
if git merge-base --is-ancestor "$rev" "$CANON_HEAD"; then
printf 'r/'
git rev-list --count --first-parent "$rev"
else
eprintf 'error: refusing to calculate r/number: %s is not an ancestor of %s\n' \
"$rev" "$CANON_HEAD" 1>&2
exit 100
fi
done
'';
# Test case, assumes that it is executed in a checkout of depot
meta.ci.extraSteps.matches-refs = {
needsOutput = true;
label = "Verify `git r` output matches refs/r/*";
command = pkgs.writeShellScript "git-r-matches-refs" ''
set -euo pipefail
export PATH="${lib.makeBinPath [ pkgs.git pkgs.findutils ]}"
revs=("origin/canon" "origin/canon~1" "93a746aaaa092ffc3e7eb37e1df30bfd3a28435f")
failed=false
# assert_eq DESCRIPTION EXPECTED GIVEN
assert_eq() {
desc="$1"
exp="$2"
given="$3"
if [[ "$exp" != "$given" ]]; then
failed=true
printf 'error: case "%s" failed\n\texp:\t%s\n\tgot:\t%s\n' "$desc" "$exp" "$given" 1>&2
fi
}
git fetch origin '+refs/r/*:refs/r/*'
for rev in "''${revs[@]}"; do
assert_eq \
"r/number ref for $rev points at that rev" \
"$(git rev-parse "$rev")" \
"$(git rev-parse "$(./result/bin/git-r "$rev")")"
done
for rev in "''${revs[@]}"; do
assert_eq \
"r/number for matches ref pointing at $rev" \
"$(git for-each-ref --points-at="$rev" --format="%(refname:short)" 'refs/r/*')" \
"$(./result/bin/git-r "$rev")"
done
assert_eq \
"Passing multiple revs to git r works as expected" \
"$(git rev-parse "''${revs[@]}")" \
"$(./result/bin/git-r "''${revs[@]}" | xargs git rev-parse)"
if $failed; then
exit 1
fi
'';
};
}
|