@@ -40,6 +40,7 @@ IDENTICAL=false
4040STABLE=false
4141DEFAULT=false
4242FORCE=false
43+ DRYRUN=false
4344UPDATE=false
4445VERBOSE=false
4546VERSION_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
18852009do_continue () {
0 commit comments