Skip to content

Commit 2f1ffad

Browse files
committed
umpf: add built-in support for synchronizing topic branches
While umpf was explicitly developed with multiple developers adding utags and sharing topic branches in mind, it is less than ideal when there are multiple developers working on the same topic branches. And it frequently leads to one of two issues: - The branch is pushed before the utag is accepted into the BSP repository and other developers get an unexpected addition to their umpf - The branch is not pushed after the utag is accepted into the BSP repository and other developers get an unexpected removal from their umpf Every time this happens, it wastes a bit of time to identify what went wrong and thus a solution built into umpf is appropriate: - CI will call umpf --remote=downstream --force push $BSP/series.inc when a PR touching a useries is accepted - Developers can call umpf pull to synchronize their topic branches or to find out when difference they have to the now upstream version Signed-off-by: Ahmad Fatoum <[email protected]>
1 parent 8044472 commit 2f1ffad

File tree

3 files changed

+167
-2
lines changed

3 files changed

+167
-2
lines changed

bash_completion

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ _umpf_completion()
3838
"")
3939
COMPREPLY=( $( compgen -W "${completion_cmds[*]} help" -- $cur ) )
4040
;;
41-
diff|show|tag|tig|build)
41+
diff|show|tag|tig|build|push|pull)
4242
local -a refs
4343
refs=( $( compgen -W "$( git for-each-ref --format='%(refname:short)' refs/tags refs/heads refs/remotes)" -- $cur ) )
4444
if [ ${#refs[@]} -eq 0 ]; then

doc/getting-started.rst

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -447,6 +447,47 @@ Or tell umpf to rebase onto a new *umpf-base* when creating a fresh *utag*::
447447
# umpf-topic-range: 8bae5bbec8cb4599c141405e9755b7c0e42e064f..19cdc2b857e662a38c712b41ce610000a5ddc6ae
448448
# umpf-end
449449

450+
Synchronizing umpf topic branch
451+
-------------------------------
452+
453+
Due to Git's distributed nature, checked out topic branches can get
454+
out-of-sync. To compare local topic branches against those referenced
455+
in a *utag*, ``umpf pull`` can be used::
456+
457+
umpf pull --dry-run 5.0/special-customer-release/20190311-1
458+
umpf: Using series from commit message...
459+
* [new branch] 02fb74aa381080855a57080138b29ecc96586788 -> v5.0/topic/most-fixes
460+
! [rejected] f0693b782dd026f2adc4d3c336d9ac6dfb352a73 -> v5.0/topic/more-fixes (non-fast-forward)
461+
462+
Following options are supported:
463+
464+
- ``--dry-run``: compare the branches, but stop short of actually updating
465+
them
466+
- ``--force``: reset local branches that are not checked-out to the
467+
``umpf-hashinfo`` in the ``utag``
468+
- ``--update``: only update existing branches
469+
470+
The counterpart to publish topic branches to a remote after creating a new
471+
``utag`` is ``umpf push``:
472+
473+
umpf --dry-run --remote=downstream push 5.0/special-customer-release/20190311-1
474+
umpf: Using series from commit message...
475+
To ssh://downstream
476+
* [new branch] 02fb74aa381080855a57080138b29ecc96586788 -> v5.0/topic/most-fixes
477+
! [rejected] f0693b782dd026f2adc4d3c336d9ac6dfb352a73 -> v5.0/topic/more-fixes (non-fast-forward)
478+
error: failed to push some refs to 'ssh:/downstream'
479+
480+
It supports the same options as ``umpf pull``, but instead of doing local
481+
changes, it operates on the specified remote.
482+
483+
``umpf push`` is especially useful when multiple developers are creating
484+
`utags` for the same project in parallel. Each developer will initially
485+
only push their `utag` to the common repository. Once the changes
486+
introduced by a `utag` are accepted, all topic branches can be force
487+
updated on the remote to this most recent `utag`, possibly via
488+
a server-side pull-request post-merge hook running, e.g.::
489+
490+
umpf push --remote=downstream --force .../linux/patches/series.inc
450491

451492
Overview
452493
--------

umpf

Lines changed: 125 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ IDENTICAL=false
4040
STABLE=false
4141
DEFAULT=false
4242
FORCE=false
43+
DRYRUN=false
4344
UPDATE=false
4445
VERBOSE=false
4546
VERSION_SEPARATOR=-
@@ -182,6 +183,8 @@ usage() {
182183
--nix with format-patch: write patch series nix
183184
-h, --help
184185
-f, --force
186+
--dry-run with push/pull: Do everything except actually send
187+
the updates.
185188
--flags specify/override umpf-flags
186189
-i, --identical use exact commit hashes, not tip of branches
187190
-s, --stable create a 'stable' tag from a branch based on an
@@ -198,6 +201,7 @@ usage() {
198201
specified, it's interpreted as
199202
<topic>=[<remote>/]<topic>
200203
-u, --update with --patchdir: update existing patches in <path>
204+
with push/pull: update only existing branches
201205
-v, --version <version> with tag: overwrite version number [default: 1]
202206
203207
Commands:
@@ -222,6 +226,8 @@ usage() {
222226
build <umpf> build an umerge from another umpf
223227
distribute <commit-ish> push patches not yet in any topic branch
224228
upstream
229+
push [<umpf>] push topic branches to the given remote
230+
pull [<umpf>] pull topic branches into the local repository
225231
226232
continue continue a previously interrupted umpf command
227233
abort abort a previously started umpf command
@@ -249,7 +255,7 @@ setup() {
249255
fi
250256

251257
o="fhilsub:n:p:r:v:"
252-
l="auto-rerere,bb,nix,flags:,default,force,help,identical,stable,update,base:,name:,patchdir:,relative:,override:,remote:,local,version:"
258+
l="auto-rerere,bb,nix,flags:,default,dry-run,force,help,identical,stable,update,base:,name:,patchdir:,relative:,override:,remote:,local,version:"
253259
if ! args="$(getopt -n umpf -o "${o}" -l "${l}" -- "${@}")"; then
254260
usage
255261
exit 1
@@ -278,6 +284,9 @@ setup() {
278284
-f|--force)
279285
FORCE=true
280286
;;
287+
--dry-run)
288+
DRYRUN=true
289+
;;
281290
--flags)
282291
FLAGS="${1}"
283292
shift
@@ -1880,6 +1889,121 @@ do_distribute() {
18801889
run_distribute
18811890
}
18821891
1892+
### namespace: push ###
1893+
1894+
push_topic() {
1895+
echo "${content}" >> "${STATE}/topic-names"
1896+
}
1897+
1898+
push_hashinfo() {
1899+
echo "${content}" >> "${STATE}/topics"
1900+
}
1901+
1902+
push_version() {
1903+
echo "${content}" > "${STATE}/tagname"
1904+
}
1905+
1906+
push_topic_range() {
1907+
echo "${content##*..}" > "${STATE}/tagrev-flat"
1908+
}
1909+
1910+
### command: push ###
1911+
1912+
resolve_commitish() {
1913+
${GIT} rev-parse --revs-only "${@}" 2>/dev/null
1914+
}
1915+
1916+
shorten_commitish() {
1917+
resolve_commitish --short "${@}"
1918+
}
1919+
1920+
do_push () {
1921+
local remote
1922+
local -a opts args branches branch_names
1923+
local -A topics
1924+
1925+
if [ -z "${GIT_REMOTE}" ]; then
1926+
info "Git remote must be specified. Cannot continue."
1927+
exit 1
1928+
fi
1929+
1930+
if [ "${GIT_REMOTE}" = "refs/heads/" ]; then
1931+
remote="${GIT_DIR}"
1932+
else
1933+
remote=${GIT_REMOTE%/}
1934+
fi
1935+
1936+
prepare_persistent push "${@}"
1937+
parse_series push "${STATE}/series"
1938+
1939+
local tagname="$(<"${STATE}/tagname")"
1940+
local tagrevf="$(<"${STATE}/tagrev-flat")"
1941+
1942+
mapfile -t branches < "${STATE}/topics"
1943+
mapfile -t branch_names < "${STATE}/topic-names"
1944+
for ((i = 0; i < ${#branch_names[@]}; i++)); do
1945+
topics["refs/heads/${branch_names[i]}"]="${branches[i]}"
1946+
done
1947+
1948+
# To determine whether the remote tag matches what we are about
1949+
# to push, we need to fetch it first to look at the flat tag's revision
1950+
if ! git fetch --quiet --no-tags "${remote}" "${tagname}" 2>/dev/null; then
1951+
abort "${remote}${remote:+/}refs/tags/${tagname} not found"
1952+
fi
1953+
1954+
local rtagrevf="$(${GIT} rev-parse --revs-only "FETCH_HEAD^" 2>/dev/null)"
1955+
if [ "${rtagrevf}" != "${tagrevf}" ]; then
1956+
abort "${remote}${remote:+/}refs/tags/${tagname} has unexpected" \
1957+
'commit hash "'"$(shorten_commitish "${rtagrevf}")"'"' \
1958+
'instead of "' "$(shorten_commitish "${tagrevf}")" '"'
1959+
fi
1960+
1961+
if $UPDATE; then
1962+
local -A rtopics
1963+
1964+
exec {lsremotefd}< <(${GIT} ls-remote "${remote}" "${!topics[@]}")
1965+
while read sha ref <&${lsremotefd}; do
1966+
rtopics["$ref"]=$sha
1967+
done
1968+
exec {lsremotefd}<&-
1969+
1970+
# Don't create any new branches, only updated existing ones
1971+
for topic in "${!topics[@]}"; do
1972+
if [[ ! -v "rtopics[$topic]" ]]; then
1973+
unset "topics[$topic]"
1974+
fi
1975+
done
1976+
fi
1977+
1978+
if ${FORCE}; then
1979+
if [ "${GIT_REMOTE}" = "refs/heads/" ]; then
1980+
# Otherwise, we'll always get rejected for "stale info"
1981+
opts+=("--force")
1982+
else
1983+
opts+=("--force-with-lease")
1984+
fi
1985+
fi
1986+
1987+
if ${DRYRUN}; then
1988+
opts+=("--dry-run")
1989+
fi
1990+
1991+
for topic in "${!topics[@]}"; do
1992+
args+=("${topics[$topic]}:${topic}")
1993+
done
1994+
1995+
# Push tag again to avoid an error if $args is empty
1996+
${GIT} push "${opts[@]}" ${remote} -- "$tagname" "${args[@]}"
1997+
1998+
cleanup
1999+
}
2000+
2001+
### command: pull ###
2002+
2003+
do_pull () {
2004+
GIT_REMOTE=refs/heads/ do_push "$@"
2005+
}
2006+
18832007
### command: continue ###
18842008
18852009
do_continue() {

0 commit comments

Comments
 (0)