Commit cbacdfb4 authored by Guido Günther's avatar Guido Günther

push: new command to push changes in one go

Closes: #733639
parent a5f22cbf
......@@ -28,6 +28,7 @@ usr/lib/python3.?/dist-packages/gbp/scripts/__init__.py usr/lib/python3/dist-pac
usr/lib/python3.?/dist-packages/gbp/scripts/pq.py usr/lib/python3/dist-packages/gbp/scripts/
usr/lib/python3.?/dist-packages/gbp/scripts/pristine_tar.py usr/lib/python3/dist-packages/gbp/scripts/
usr/lib/python3.?/dist-packages/gbp/scripts/pull.py usr/lib/python3/dist-packages/gbp/scripts/
usr/lib/python3.?/dist-packages/gbp/scripts/push.py usr/lib/python3/dist-packages/gbp/scripts/
usr/lib/python3.?/dist-packages/gbp/scripts/supercommand.py usr/lib/python3/dist-packages/gbp/scripts/
usr/lib/python3.?/dist-packages/gbp/tmpfile.py usr/lib/python3/dist-packages/gbp/
usr/lib/python3.?/dist-packages/gbp/tristate.py usr/lib/python3/dist-packages/gbp/
......
......@@ -12,4 +12,5 @@ docs/gbp-import-orig.1
docs/gbp-pq.1
docs/gbp-pristine-tar.1
docs/gbp-pull.1
docs/gbp-push.1
docs/git-pbuilder.1
......@@ -14,6 +14,7 @@ MAN1S = \
gbp-pq \
gbp-pristine-tar \
gbp-pull \
gbp-push \
gbp-buildpackage-rpm \
gbp-import-srpm \
gbp-pq-rpm \
......
......@@ -19,6 +19,7 @@
<!ENTITY gbp-pq-import "<command>gbp&nbsp;pq&nbsp<option>import</option></command>">
<!ENTITY gbp-pq-rebase "<command>gbp&nbsp;pq&nbsp<option>rebase</option></command>">
<!ENTITY gbp-pq-export "<command>gbp&nbsp;pq&nbsp<option>export</option></command>">
<!ENTITY gbp-push "<command>gbp&nbsp;push</command>">
<!ENTITY gbp-pristine-tar "<command>gbp&nbsp;pristine-tar</command>">
<!ENTITY gbp-create-remote-repo "<command>gbp&nbsp;create-remote-repo</command>">
<!ENTITY git-pbuilder "<command>git-pbuilder</command>">
......
<!DOCTYPE reference PUBLIC "-//OASIS//DTD DocBook V4.1//EN" [
<!ENTITY % COMMON SYSTEM "common.ent">
%COMMON;
<!ENTITY % MANPAGES SYSTEM "manpages/manpages.ent">
%MANPAGES;
]>
<reference>
<title>git-buildpackage Manual</title>
&man.gbp.push;
</reference>
<refentry id="man.gbp.push">
<refentryinfo>
<address>
&dhemail;
</address>
<author>
&dhfirstname;
&dhsurname;
</author>
</refentryinfo>
<refmeta>
<refentrytitle>gbp-push</refentrytitle>
&dhsection;
</refmeta>
<refnamediv>
<refname>gbp-push</refname>
<refpurpose>Push &debian; packaging changes to a &git; remote</refpurpose>
</refnamediv>
<refsynopsisdiv>
<cmdsynopsis>
&gbp-push;
&man.common.options.synopsis;
<arg><option>--ignore-branch</option></arg>
<arg><option>--debian-branch=</option><replaceable>branch_name</replaceable></arg>
<arg><option>--upstream-branch=</option><replaceable>branch_name</replaceable></arg>
<arg><option>--debian-tag=</option><replaceable>tag-format</replaceable></arg>
<arg><option>--upstream-tag=</option><replaceable>tag-format</replaceable></arg>
<arg><option>--[no-]pristine-tar</option></arg>
<arg><option>--dry-run</option></arg>
<arg><replaceable>repository</replaceable></arg>
</cmdsynopsis>
</refsynopsisdiv>
<refsect1>
<title>DESCRIPTION</title>
<para>
&gbp-push; pushes your local changes to a remote repository. It
is best run after uploading a &debian; package to the archive to
update you &debian-branch;, &upstream-branch;, &pristine-tar;
branch and corresponding tags. It will in order
</para>
<itemizedlist>
<listitem>
<para>
Verify that it is being executed from the &debian-branch;.
</para>
</listitem>
<listitem>
<para>
Determine the debian tag from <filename>debian/changelog</filename>
and add it the list of things to push if the changelog does not indicate
an unreleased package.
</para>
</listitem>
<listitem>
<para>
Determine the upstream tag from <filename>debian/changelog</filename>
and add it to the list of things to push if it's not a native package.
</para>
</listitem>
<listitem>
<para>
Determine if the tags correspond to the branch tips of the corresponding
upstream and debian branches. If so, these branches will be added to the list
of things to push. If not the changes will only be pushed up to the commit
that is referenced by the corresponding tag.
</para>
</listitem>
<listitem>
<para>
Determine if the pristine-tar branch needs to be pushed and if so adds it
to the list of things to push.
</para>
</listitem>
<listitem>
<para>
Finally, if not in dry-run mode, pushes the above changes to the remote side.
</para>
</listitem>
</itemizedlist>
<para>
If a <replaceable>remote</replaceable> is given on the command line
the changes are pushed to the given remote repository. By
default it will push to the current branchs remote and fall
back to <emphasis>origin</emphasis>.
</para>
</refsect1>
<refsect1>
<title>OPTIONS</title>
<variablelist>
&man.common.options.description;
<varlistentry>
<term><option>--ignore-branch</option></term>
<listitem>
<para>
Don't fail if the &debian-branch; does not match the currently checked out
branach and push the current branch instead.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--debian-branch</option>=<replaceable>branch_name</replaceable></term>
<listitem>
<para>
The branch in the Git repository the Debian package is being
developed on.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--debian-tag=</option><replaceable>TAG-FORMAT</replaceable></term>
<listitem>
<para>
Use this tag format when looking for tags corresponding to a &debian;
version. Default is <replaceable>debian/%(version)s</replaceable>.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--upstream-branch</option>=<replaceable>branch_name</replaceable></term>
<listitem>
<para>
The branch in the &git; repository the upstream sources are put
onto.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--upstream-tag=</option><replaceable>TAG-FORMAT</replaceable></term>
<listitem>
<para>
Use this tag format when looking for tags of upstream
versions. Default
is <replaceable>upstream/%(version)s</replaceable>.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--pristine-tar</option></term>
<listitem>
<para>Whether to update the pristine-tar branch too.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--dry-run</option></term>
<listitem>
<para>
Pass the <option>--dry-run</option> to &gitcmd; <option>push</option>. So don't
push anything, only check if things are pushable.
</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
&man.gbp.config-files;
</refsect1>
<refsect1>
<title>SEE ALSO</title>
<para>
<xref linkend="man.gbp.buildpackage"/,
<xref linkend="man.gbp.clone"/,
<xref linkend="man.gbp.pull"/,
<xref linkend="man.gbp.conf"/
</para>
</refsect1>
<refsect1>
<title>AUTHOR</title>
<para>&dhusername; &dhemail;</para>
</refsect1>
</refentry>
......@@ -164,6 +164,7 @@
<xref linkend="man.gbp.export.orig"/,
<xref linkend="man.gbp.create.remote.repo"/,
<xref linkend="man.gbp.pull"/,
<xref linkend="man.gbp.push"/,
<xref linkend="man.gbp.clone"/,
<citerefentry>
<refentrytitle>git-pbuilder</refentrytitle>
......
......@@ -8,6 +8,7 @@
<!ENTITY man.gbp SYSTEM "gbp.sgml">
<!ENTITY man.gbp.pristine.tar SYSTEM "gbp-pristine-tar.sgml">
<!ENTITY man.gbp.pull SYSTEM "gbp-pull.sgml">
<!ENTITY man.gbp.push SYSTEM "gbp-push.sgml">
<!ENTITY man.gbp.clone SYSTEM "gbp-clone.sgml">
<!ENTITY man.gbp.pq SYSTEM "gbp-pq.sgml">
<!ENTITY man.gbp.create.remote.repo SYSTEM "gbp-create-remote-repo.sgml">
......
......@@ -37,6 +37,7 @@
&man.gbp.clone;
&man.gbp.config;
&man.gbp.pull;
&man.gbp.push;
&man.gbp.pristine.tar;
&man.gbp.exportorig;
&man.gbp.pq;
......
......@@ -312,6 +312,15 @@ class DebianGitRepository(PkgGitRepository):
except CommandExecFailed as e:
raise GitRepositoryError(str(e))
def get_pristine_tar_commit(self, source, component=None):
"""
Get the pristine-tar commit for the given source package's latest version.
"""
comp = '-%s' % component if component else ''
return self.pristine_tar.get_commit('%s_%s.orig%s.tar.*' % (source.sourcepkg,
source.upstream_version,
comp))
def create_upstream_tarball_via_pristine_tar(self, source, output_dir, comp, component=None):
output = source.upstream_tarball_name(comp.type, component=component)
try:
......
#!/usr/bin/python3
# vim: set fileencoding=utf-8 :
#
# (C) 2017 Guido Günther <agx@sigxcpu.org>
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, please see
# <http://www.gnu.org/licenses/>
"""Push your changes to a remote"""
import os
import sys
import gbp.log
from gbp.config import GbpOptionParserDebian
from gbp.deb.git import DebianGitRepository, GitRepositoryError
from gbp.deb.source import DebianSource
from gbp.errors import GbpError
from gbp.scripts.common import ExitCodes
def build_parser(name):
try:
parser = GbpOptionParserDebian(command=os.path.basename(name),
usage='%prog [options]')
except GbpError as err:
gbp.log.err(err)
return None
parser.add_option("-d", "--dry-run", dest="dryrun", default=False,
action="store_true", help="dry run, don't push.")
parser.add_config_file_option(option_name="upstream-branch",
dest="upstream_branch")
parser.add_config_file_option(option_name="upstream-tag",
dest="upstream_tag")
parser.add_config_file_option(option_name="debian-branch",
dest="debian_branch")
parser.add_config_file_option(option_name="debian-tag",
dest="debian_tag")
parser.add_boolean_config_file_option(option_name="pristine-tar",
dest="pristine_tar")
parser.add_boolean_config_file_option(option_name="ignore-branch", dest="ignore_branch")
parser.add_config_file_option(option_name="color", dest="color", type='tristate')
parser.add_config_file_option(option_name="color-scheme",
dest="color_scheme")
parser.add_option("--verbose", action="store_true", dest="verbose",
default=False, help="verbose command execution")
return parser
def parse_args(argv):
parser = build_parser(argv[0])
if not parser:
return None, None
return parser.parse_args(argv)
def do_push(repo, dests, to_push, dry_run):
verb = "Dry-run: Pushing" if dry_run else "Pushing"
for dest in dests:
for tag in to_push['tags']:
gbp.log.info("%s %s to %s" % (verb, tag, dest))
repo.push_tag(dest, tag, dry_run=dry_run)
for k, v in to_push['refs'].items():
gbp.log.info("%s %s to %s:%s" % (verb, v, dest, k))
repo.push(dest, v, k, dry_run=dry_run)
def get_push_src(repo, ref, tag):
"""
Determine wether we can push the ref
If the ref is further ahead than the tag
we only want to push up to this tag.
"""
commit = repo.rev_parse("%s^{commit}" % tag)
if repo.rev_parse(ref) == commit:
return ref
else:
return commit
def get_remote(repo, branch):
remote_branch = repo.get_merge_branch(branch)
return remote_branch.split('/')[0] if remote_branch else 'origin'
def main(argv):
retval = 1
branch = None
dest = None
to_push = {
'refs': {},
'tags': [],
}
(options, args) = parse_args(argv)
if not options:
return ExitCodes.parse_error
if len(args) > 2:
gbp.log.err("Only a single remote repository can be given")
elif len(args) == 2:
dest = args[1]
gbp.log.setup(options.color, options.verbose, options.color_scheme)
try:
repo = DebianGitRepository(os.path.curdir, toplevel=False)
except GitRepositoryError:
gbp.log.err("%s is not inside a git repository" % (os.path.abspath('.')))
return 1
try:
source = DebianSource(repo.path)
branch = repo.get_branch()
if not options.ignore_branch:
if branch != options.debian_branch:
gbp.log.err("You are not on branch '%s' but on '%s'" % (options.debian_branch, branch))
raise GbpError("Use --git-ignore-branch to ignore or --git-debian-branch to set the branch name.")
if not dest:
dest = get_remote(repo, branch)
dtag = repo.version_to_tag(options.debian_tag, source.version)
if repo.has_tag(dtag):
to_push['tags'].append(dtag)
if source.is_releasable() and branch:
ref = 'refs/heads/%s' % branch
to_push['refs'][ref] = get_push_src(repo, ref, dtag)
if not source.is_native():
utag = repo.version_to_tag(options.upstream_tag,
source.upstream_version)
if repo.has_tag(utag):
to_push['tags'].append(utag)
ref = 'refs/heads/%s' % options.upstream_branch
to_push['refs'][ref] = get_push_src(repo, ref, utag)
if options.pristine_tar:
commit = repo.get_pristine_tar_commit(source)
if commit:
ref = 'refs/heads/pristine-tar'
to_push['refs'][ref] = get_push_src(repo, ref, commit)
do_push(repo,
[dest],
to_push,
dry_run=options.dryrun)
retval = 0
except (GbpError, GitRepositoryError) as err:
if str(err):
gbp.log.err(err)
return retval
if __name__ == '__main__':
sys.exit(main(sys.argv))
# vim:et:ts=4:sw=4:et:sts=4:ai:set list listchars=tab\:»·,trail\:·:
......@@ -225,6 +225,7 @@ done
%{python_sitelib}/gbp/scripts/config.py*
%{python_sitelib}/gbp/scripts/pristine_tar.py*
%{python_sitelib}/gbp/scripts/pull.py*
%{python_sitelib}/gbp/scripts/push.py*
%{python_sitelib}/gbp/scripts/supercommand.py*
%{python_sitelib}/gbp/scripts/common/*.py*
%{python_sitelib}/gbp/git/*.py*
......@@ -236,6 +237,7 @@ done
%{_mandir}/man1/gbp-config.1*
%{_mandir}/man1/gbp-pristine-tar.1*
%{_mandir}/man1/gbp-pull.1*
%{_mandir}/man1/gbp-push.1*
%{_mandir}/man5/*.5*
%endif
......
# vim: set fileencoding=utf-8 :
#
# (C) 2017 Guido Günther <agx@sigxcpu.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, please see
# <http://www.gnu.org/licenses/>
import subprocess
from tests.component import ComponentTestBase
from tests.component.deb.fixtures import RepoFixtures
from gbp.git import GitRepository
from gbp.scripts.push import main as push
class TestPush(ComponentTestBase):
"""Test pushing to remote repos"""
def setUp(self):
ComponentTestBase.setUp(self)
self.target = GitRepository.create('target', bare=True)
@RepoFixtures.native()
def test_push_native(self, repo):
repo.add_remote_repo('origin', self.target.path)
self.assertEquals(push(['argv0']), 0)
self._check_repo_state(self.target, 'master',
['master'],
tags=['debian/0.4.14'])
self.assertEquals(repo.head, self.target.head)
@RepoFixtures.quilt30()
def test_push_upstream(self, repo):
repo.add_remote_repo('origin', self.target.path)
self.assertEquals(push(['argv0']), 0)
self._check_repo_state(self.target, 'master',
['master', 'upstream'],
tags=['debian/2.8-1', 'upstream/2.8'])
self.assertEquals(repo.head, self.target.head)
@RepoFixtures.quilt30(opts=['--pristine-tar'])
def test_push_pristine_tar(self, repo):
repo.add_remote_repo('origin', self.target.path)
self.assertEquals(push(['argv0', '--pristine-tar']), 0)
self._check_repo_state(self.target, 'master',
['master', 'upstream', 'pristine-tar'],
tags=['debian/2.8-1', 'upstream/2.8'])
self.assertEquals(repo.head, self.target.head)
@RepoFixtures.native()
def test_push_tag_ne_branch(self, repo):
repo.add_remote_repo('origin', self.target.path)
self.add_file(repo, "foo.txt", "foo")
self.assertEquals(push(['argv0']), 0)
self._check_repo_state(self.target, 'master',
['master'],
tags=['debian/0.4.14'])
self.assertEquals(repo.rev_parse("HEAD^"),
self.target.head)
@RepoFixtures.quilt30()
def test_not_debian_branch(self, repo):
repo.add_remote_repo('origin', self.target.path)
repo.create_branch("foo")
repo.set_branch("foo")
self.assertEquals(push(['argv0']), 1)
self._check_log(-2, ".*You are not on branch 'master' but on 'foo'")
@RepoFixtures.quilt30()
def test_dont_push_unreleased(self, repo):
repo.add_remote_repo('origin', self.target.path)
subprocess.check_call(["debchange", "-i", "foo"])
self.assertEquals(push(['argv0']), 0)
self._check_repo_state(self.target, None,
['upstream'],
tags=['upstream/2.8'])
@RepoFixtures.quilt30()
def test_push_not_origin(self, repo):
repo.add_remote_repo('notorigin', self.target.path)
self.assertEquals(push(['argv0', 'notorigin']), 0)
self._check_repo_state(self.target, 'master',
['master', 'upstream'],
tags=['debian/2.8-1', 'upstream/2.8'])
self.assertEquals(repo.head, self.target.head)
@RepoFixtures.quilt30()
def test_push_not_origin_detect(self, repo):
repo.add_remote_repo('notorigin', self.target.path)
repo.set_config("branch.master.remote", "notorigin")
repo.set_config("branch.master.merge", "refs/heads/master")
self.assertEquals(push(['argv0']), 0)
self._check_repo_state(self.target, 'master',
['master', 'upstream'],
tags=['debian/2.8-1', 'upstream/2.8'])
self.assertEquals(repo.head, self.target.head)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment