I looked into implementing the discussed gbp import-git subcommand. Find
attached preliminary patches. In my tests, the new subcommand tags the
upstream branch and imports it correctly, but perhaps I missed something.
Filtering is not yet considered at all. Feedback/review welcome (or, feel
free to just run with them if that’s easier. I’m not fast with Python.)

One thing came up during the implementation to which I’d like your
feedback: if the upstream branch isn’t by chance already tagged with a
proper version number (e.g. v1.2), how do we get a suitable version number
for the git snapshot we’re about to package? We have such logic in
dh-make-golang:
https://github.com/Debian/dh-make-golang/blob/5a5180dce36c9c878b7551640ff018a6dfabf0dd/version.go#L19
— as you can see it’s rather complicated. I couldn’t find anything
comparable in git-buildpackage yet. Did I miss it? If no, could such logic
be added?

Thanks,

On Thu, Jan 25, 2018 at 1:04 PM, Michael Stapelberg <[email protected]>
wrote:

>
>
> On Thu, Jan 25, 2018 at 12:46 PM, Guido Günther <[email protected]> wrote:
>
>> Hi,
>> On Thu, Jan 25, 2018 at 10:25:09AM +0100, Raphael Hertzog wrote:
>> > > • branch “upstream/latest” tracks an upstream branch, e.g. remote
>> > > “upstream”, branch “master”.
>> > >
>> > > • branch “upstream/latest-filtered” contains a filtered version of
>> that
>> > > branch, whose commits would be tagged e.g. upstream/1.0+dfsg1 so that
>> > > gbp-buildpackage picks the correct commit.
>> > >
>> > > I think that complies with DEP14 as good as possible, and is still
>> > > reasonably clear for casual users. Thoughts?
>> >
>> > I would use "upstream-filtered/latest" for the latter. You have clean
>> > namespace separation. But this still changes the assumption about
>> > upstream/latest (non-filtered now) so it needs a broader discussion IMO.
>>
>> To add to the bikeshed: I like path like separation in git:
>>
>>     upstream/filtered/latest
>>
>> or (since upstream is somewhat redundant):
>>
>
> I don’t feel that “upstream” is redundant. I think the contents of a
> branch should be obvious from the name.
>
>
>>
>>     filtered/latest
>>     pristine/latest
>>
>
> The “pristine” namespace could easily be confused with “pristine-tar”, so
> I’d prefer avoiding that name altogether if possible.
>
>
>>
>> this would also allow to retain upstream/ with the original meaning for
>>
>
> Wait, now I’m confused. Isn’t “upstream” the same as “upstream/latest”?
> What “original meaning” are you referring to? :)
>
>
>> existing projects and would make the switch of defaults easier since
>> gbp's current default upstream does not conflict with upstream/latest.
>>
>> Cheers,
>>  -- Guido
>>
>
>
>
> --
> Best regards,
> Michael
>



-- 
Best regards,
Michael
From 6c59a6b196382f846b48f1a4d650fa6937d360dc Mon Sep 17 00:00:00 2001
From: Michael Stapelberg <[email protected]>
Date: Fri, 26 Jan 2018 22:36:17 +0100
Subject: [PATCH 1/2] Copy import_orig.py to import_git.py

---
 gbp/scripts/import_git.py | 669 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 669 insertions(+)
 create mode 100644 gbp/scripts/import_git.py

diff --git a/gbp/scripts/import_git.py b/gbp/scripts/import_git.py
new file mode 100644
index 0000000..97b6d54
--- /dev/null
+++ b/gbp/scripts/import_git.py
@@ -0,0 +1,669 @@
+# vim: set fileencoding=utf-8 :
+#
+# (C) 2006, 2007, 2009, 2011, 2015, 2016 Guido Guenther <[email protected]>
+#    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 a new upstream version into a Git repository"""
+
+import os
+import shutil
+import sys
+import tempfile
+import time
+import gbp.command_wrappers as gbpc
+from gbp.deb import (DebianPkgPolicy, parse_changelog_repo)
+from gbp.deb.format import DebianSourceFormat
+from gbp.deb.upstreamsource import DebianUpstreamSource, unpack_component_tarball
+from gbp.deb.uscan import (Uscan, UscanError)
+from gbp.deb.changelog import ChangeLog, NoChangeLogError
+from gbp.deb.git import (GitRepositoryError, DebianGitRepository)
+from gbp.config import GbpOptionParserDebian, GbpOptionGroup, no_upstream_branch_msg
+from gbp.errors import GbpError
+from gbp.format import format_str
+from gbp.git.vfs import GitVfs
+import gbp.log
+from gbp.scripts.common import ExitCodes, is_download, get_component_tarballs
+from gbp.scripts.common.import_orig import (orig_needs_repack, cleanup_tmp_tree,
+                                            ask_package_name, ask_package_version,
+                                            repack_upstream, is_link_target, download_orig)
+from gbp.scripts.common.hook import Hook
+
+
+class RollbackError(GitRepositoryError):
+    """
+    Error raised if the rollback failed
+    """
+    def __init__(self, errors):
+        self.msg = "Automatic rollback failed"
+        super(RollbackError, self).__init__(self.msg)
+        self.errors = errors
+
+    def __str__(self):
+        return "%s %s" % (self.msg, self.errors)
+
+
+class ImportOrigDebianGitRepository(DebianGitRepository):
+    """
+    Like a DebianGitRepository but can also perform rollbacks and knows
+    about some of the inner workings upstream vcs_tag, …
+    """
+    def __init__(self, *args, **kwargs):
+        self.rollbacks = []
+        self.rollback_errors = []
+        DebianGitRepository.__init__(self, *args, **kwargs)
+
+    def has_rollbacks(self):
+        return len(self.rollbacks) > 0
+
+    def vcs_tag_parent(self, vcs_tag_format, version):
+        """If linking to the upstream VCS get the commit id"""
+        if vcs_tag_format:
+            return [self.rev_parse("%s^{}" % self.version_to_tag(vcs_tag_format, version))],
+        else:
+            return None
+
+    def rrr(self, refname, action, reftype):
+        """
+        Remember ref for rollback
+
+        @param refname: ref to roll back
+        @param action: the rollback action (delete, reset, ...)
+        @param reftype: the reference type (tag, branch, ...)
+        """
+        sha = None
+
+        if action == 'reset':
+            try:
+                sha = self.rev_parse(refname)
+            except GitRepositoryError as err:
+                gbp.log.warn("Failed to rev-parse %s: %s" % (refname, err))
+        elif action == 'delete':
+            pass
+        elif action == 'abortmerge':
+            pass
+        else:
+            raise GbpError("Unknown action %s for %s %s" % (action, reftype, refname))
+        self.rollbacks.append((refname, reftype, action, sha))
+
+    def rrr_branch(self, branchname, action='reset-or-delete'):
+        if action == 'reset-or-delete':
+            if self.has_branch(branchname):
+                return self.rrr(branchname, 'reset', 'branch')
+            else:
+                return self.rrr(branchname, 'delete', 'branch')
+        else:
+            return self.rrr(branchname, action, 'branch')
+
+    def rrr_tag(self, tagname, action='delete'):
+        return self.rrr(tagname, action, 'tag')
+
+    def rrr_merge(self, commit, action='abortmerge'):
+        return self.rrr(commit, action, 'commit')
+
+    def rollback(self):
+        """
+        Perform a complete rollback
+
+        Try to roll back as much as possible and remember what failed.
+        """
+        for (name, reftype, action, sha) in self.rollbacks:
+            try:
+                if action == 'delete':
+                    gbp.log.info('Rolling back %s %s by deleting it' % (reftype, name))
+                    if reftype == 'tag':
+                        self.delete_tag(name)
+                    elif reftype == 'branch':
+                        self.delete_branch(name)
+                    else:
+                        raise GitRepositoryError("Don't know how to delete %s %s" % (reftype, name))
+                elif action == 'reset' and reftype == 'branch':
+                    gbp.log.info('Rolling back branch %s by resetting it to %s' % (name, sha))
+                    self.update_ref("refs/heads/%s" % name, sha, msg="gbp import-orig: failure rollback of %s" % name)
+                elif action == 'abortmerge':
+                    if self.is_in_merge():
+                        gbp.log.info('Rolling back failed merge of %s' % name)
+                        self.abort_merge()
+                    else:
+                        gbp.log.info("Nothing to rollback for merge of '%s'" % name)
+                else:
+                    raise GitRepositoryError("Don't know how to %s %s %s" % (action, reftype, name))
+            except GitRepositoryError as e:
+                self.rollback_errors.append((name, reftype, action, sha, e))
+        if self.rollback_errors:
+            raise RollbackError(self.rollback_errors)
+
+    # Wrapped methods for rollbacks
+    def create_tag(self, *args, **kwargs):
+        name = kwargs['name']
+        ret = super(ImportOrigDebianGitRepository, self).create_tag(*args, **kwargs)
+        self.rrr_tag(name)
+        return ret
+
+    def commit_dir(self, *args, **kwargs):
+        import_branch = kwargs['branch']
+        self.rrr_branch(import_branch)
+        return super(ImportOrigDebianGitRepository, self).commit_dir(*args, **kwargs)
+
+    def create_branch(self, *args, **kwargs):
+        branch = kwargs['branch']
+        ret = super(ImportOrigDebianGitRepository, self).create_branch(*args, **kwargs)
+        self.rrr_branch(branch, 'delete')
+        return ret
+
+    def merge(self, *args, **kwargs):
+        commit = args[0] if args else kwargs['commit']
+        try:
+            return super(ImportOrigDebianGitRepository, self).merge(*args, **kwargs)
+        except GitRepositoryError:
+            # Only cleanup in the error case to undo working copy
+            # changes. Resetting the refs handles the other cases.
+            self.rrr_merge(commit)
+            raise
+
+
+def prepare_pristine_tar(archive, pkg, version):
+    """
+    Prepare the upstream source for pristine tar import.
+
+    This checks if the upstream source is actually a tarball
+    and creates a symlink from I{archive}
+    to I{<pkg>_<version>.orig.tar.<ext>} so pristine-tar will
+    see the correct basename.
+
+    @param archive: the upstream source's name
+    @type archive: C{str}
+    @param pkg: the source package's name
+    @type pkg: C{str}
+    @param version: the upstream version number
+    @type version: C{str}
+    @rtype: C{str}
+    """
+    linked = False
+    if os.path.isdir(archive):
+        return None, None
+
+    ext = os.path.splitext(archive)[1]
+    if ext in ['.tgz', '.tbz2', '.tlz', '.txz']:
+        ext = ".%s" % ext[2:]
+
+    link = "../%s_%s.orig.tar%s" % (pkg, version, ext)
+
+    if os.path.basename(archive) != os.path.basename(link):
+        try:
+            if not is_link_target(archive, link):
+                if os.path.exists(link):
+                    backup = "%s.%d" % (link, time.time())
+                    gbp.log.info("%s already exists, moving to %s" % (link, backup))
+                    shutil.move(link, backup)
+                os.symlink(os.path.abspath(archive), link)
+                linked = True
+        except OSError as err:
+                raise GbpError("Cannot symlink '%s' to '%s': %s" % (archive, link, err[1]))
+        return (link, linked)
+    else:
+        return (archive, linked)
+
+
+def upstream_import_commit_msg(options, version):
+    return options.import_msg % dict(version=version)
+
+
+def detect_name_and_version(repo, source, options):
+    # Guess defaults for the package name and version from the
+    # original tarball.
+    guessed_package, guessed_version = source.guess_version()
+
+    # Try to find the source package name
+    try:
+        cp = ChangeLog(filename='debian/changelog')
+        sourcepackage = cp['Source']
+    except NoChangeLogError:
+        try:
+            # Check the changelog file from the repository, in case
+            # we're not on the debian-branch (but upstream, for
+            # example).
+            cp = parse_changelog_repo(repo, options.debian_branch, 'debian/changelog')
+            sourcepackage = cp['Source']
+        except NoChangeLogError:
+            if options.interactive:
+                sourcepackage = ask_package_name(guessed_package,
+                                                 DebianPkgPolicy.is_valid_packagename,
+                                                 DebianPkgPolicy.packagename_msg)
+            else:
+                if guessed_package:
+                    sourcepackage = guessed_package
+                else:
+                    raise GbpError("Couldn't determine upstream package name. Use --interactive.")
+
+    # Try to find the version.
+    if options.version:
+        version = options.version
+    else:
+        if options.interactive:
+            version = ask_package_version(guessed_version,
+                                          DebianPkgPolicy.is_valid_upstreamversion,
+                                          DebianPkgPolicy.upstreamversion_msg)
+        else:
+            if guessed_version:
+                version = guessed_version
+            else:
+                raise GbpError("Couldn't determine upstream version. Use '-u<version>' or --interactive.")
+
+    return (sourcepackage, version)
+
+
+def find_upstream(use_uscan, args):
+    """Find the main tarball to import - either via uscan or via command line argument
+    @return: upstream source filename or None if nothing to import
+    @rtype: string
+    @raise GbpError: raised on all detected errors
+
+    >>> find_upstream(False, ['too', 'many'])
+    Traceback (most recent call last):
+    ...
+    gbp.errors.GbpError: More than one archive specified. Try --help.
+    >>> find_upstream(False, [])
+    Traceback (most recent call last):
+    ...
+    gbp.errors.GbpError: No archive to import specified. Try --help.
+    >>> find_upstream(True, ['tarball'])
+    Traceback (most recent call last):
+    ...
+    gbp.errors.GbpError: you can't pass both --uscan and a filename.
+    >>> find_upstream(False, ['tarball']).path
+    'tarball'
+    """
+    if use_uscan:
+        if args:
+            raise GbpError("you can't pass both --uscan and a filename.")
+
+        uscan = Uscan()
+        gbp.log.info("Launching uscan...")
+        try:
+            if not uscan.scan():
+                gbp.log.info("package is up to date, nothing to do.")
+                return None
+        except UscanError as e:
+            raise GbpError("%s" % e)
+
+        if uscan.tarball:
+            gbp.log.info("Using uscan downloaded tarball %s" % uscan.tarball)
+            args.append(uscan.tarball)
+        else:
+            raise GbpError("uscan didn't download anything, and no source was found in ../")
+    if len(args) > 1:  # source specified
+        raise GbpError("More than one archive specified. Try --help.")
+    elif len(args) == 0:
+        raise GbpError("No archive to import specified. Try --help.")
+    else:
+        return DebianUpstreamSource(args[0])
+
+
+def debian_branch_merge(repo, tag, version, options):
+    try:
+        func = globals()["debian_branch_merge_by_%s" % options.merge_mode]
+    except KeyError:
+        raise GbpError("%s is not a valid merge mode" % options.merge_mode)
+    func(repo, tag, version, options)
+
+
+def postimport_hook(repo, tag, version, options):
+    if options.postimport:
+        epoch = ''
+        if os.access('debian/changelog', os.R_OK):
+            # No need to check the changelog file from the
+            # repository, since we're certain that we're on
+            # the debian-branch
+            cp = ChangeLog(filename='debian/changelog')
+            if cp.has_epoch():
+                epoch = '%s:' % cp.epoch
+        debian_version = "%s%s-1" % (epoch, version)
+        info = {'version': debian_version}
+        env = {'GBP_BRANCH': options.debian_branch,
+               'GBP_TAG': tag,
+               'GBP_UPSTREAM_VERSION': version,
+               'GBP_DEBIAN_VERSION': debian_version,
+               }
+        Hook('Postimport',
+             format_str(options.postimport, info),
+             extra_env=env)()
+
+
+def is_30_quilt(repo, options):
+    format_file = DebianSourceFormat.format_file
+    try:
+        content = GitVfs(repo, options.debian_branch).open(format_file).read()
+    except IOError:
+        return False
+    return str(DebianSourceFormat(content)) == "3.0 (quilt)"
+
+
+def debian_branch_merge_by_auto(repo, tag, version, options):
+    if is_30_quilt(repo, options):
+        gbp.log.debug("3.0 (quilt) package, replacing debian/ dir")
+        return debian_branch_merge_by_replace(repo, tag, version, options)
+    else:
+        gbp.log.debug("not 3.0 (quilt) package, using git merge")
+        return debian_branch_merge_by_merge(repo, tag, version, options)
+
+
+def debian_branch_merge_by_replace(repo, tag, version, options):
+    gbp.log.info("Replacing upstream source on '%s'" % options.debian_branch)
+
+    tree = [x for x in repo.list_tree("%s^{tree}" % tag)
+            if x[-1] != b'debian']
+    msg = "Update upstream source from tag '%s'" % (tag)
+
+    # Get the current debian/ tree on the debian branch
+    try:
+        deb_sha = [x for x in repo.list_tree("%s^{tree}" % options.debian_branch)
+                   if x[-1] == b'debian' and x[1] == 'tree'][0][2]
+        gbp.log.debug("Using %s as debian/ tree" % deb_sha)
+        tree.append(['040000', 'tree', deb_sha, 'debian'])
+        msg += "\n\nUpdate to upstream version '%s'\nwith Debian dir %s" % (version, deb_sha)
+    except IndexError:
+        pass  # no debian/ dir is fine
+
+    sha = repo.make_tree(tree)
+    commit = repo.commit_tree(sha, msg, ["%s^{commit}" % options.debian_branch,
+                                         "%s^{commit}" % tag])
+    repo.update_ref("refs/heads/%s" % options.debian_branch, commit,
+                    msg="gbp: Updating %s after import of %s" % (options.debian_branch,
+                                                                 tag))
+    repo.force_head(commit, hard=True)
+
+
+def debian_branch_merge_by_merge(repo, tag, version, options):
+    gbp.log.info("Merging to '%s'" % options.debian_branch)
+    branch = repo.get_branch()
+    repo.set_branch(options.debian_branch)
+    try:
+        repo.merge(tag)
+    except GitRepositoryError:
+        raise GbpError("Automatic merge failed.")
+    repo.set_branch(branch)
+
+
+def unpack_tarballs(sourcepackage, source, version, component_tarballs, options):
+    tmpdir = tempfile.mkdtemp(dir='../')
+    if not source.is_dir():  # Unpack main tarball
+        source.unpack(tmpdir, options.filters)
+        gbp.log.debug("Unpacked '%s' to '%s'" % (source.path, source.unpacked))
+
+    if orig_needs_repack(source, options):
+        gbp.log.debug("Filter pristine-tar: repacking '%s' from '%s'" % (source.path, source.unpacked))
+        (source, tmpdir) = repack_upstream(source, sourcepackage, version, tmpdir, options.filters)
+
+    if not source.is_dir():  # Unpack component tarballs
+        for (component, tarball) in component_tarballs:
+            unpack_component_tarball(source.unpacked, component, tarball, options.filters)
+    return (source, tmpdir)
+
+
+def set_bare_repo_options(options):
+    """Modify options for import into a bare repository"""
+    if options.pristine_tar or options.merge:
+        gbp.log.info("Bare repository: setting %s%s options"
+                     % (["", " '--no-pristine-tar'"][options.pristine_tar],
+                        ["", " '--no-merge'"][options.merge]))
+        options.pristine_tar = False
+        options.merge = False
+
+
+def rollback(repo, options):
+    if repo and repo.has_rollbacks() and options.rollback:
+        gbp.log.err("Error detected, Will roll back changes.")
+        try:
+            repo.rollback()
+            # Make sure the very last line as an error message
+            gbp.log.err("Rolled back changes after import error.")
+        except Exception as e:
+            gbp.log.err("%s" % e)
+            gbp.log.err("Clean up manually and please report a bug: %s" %
+                        repo.rollback_errors)
+
+
+def build_parser(name):
+    try:
+        parser = GbpOptionParserDebian(command=os.path.basename(name), prefix='',
+                                       usage='%prog [options] /path/to/upstream-version.tar.gz | --uscan')
+    except GbpError as err:
+        gbp.log.err(err)
+        return None
+
+    import_group = GbpOptionGroup(parser, "import options",
+                                  "pristine-tar and filtering")
+    tag_group = GbpOptionGroup(parser, "tag options",
+                               "options related to git tag creation")
+    branch_group = GbpOptionGroup(parser, "version and branch naming options",
+                                  "version number and branch layout options")
+    cmd_group = GbpOptionGroup(parser, "external command options",
+                               "how and when to invoke external commands and hooks")
+    for group in [import_group, branch_group, tag_group, cmd_group]:
+        parser.add_option_group(group)
+
+    branch_group.add_option("-u", "--upstream-version", dest="version",
+                            help="Upstream Version")
+    branch_group.add_config_file_option(option_name="debian-branch",
+                                        dest="debian_branch")
+    branch_group.add_config_file_option(option_name="upstream-branch",
+                                        dest="upstream_branch")
+    branch_group.add_config_file_option(option_name="upstream-vcs-tag", dest="vcs_tag",
+                                        help="Upstream VCS tag add to the merge commit")
+    branch_group.add_boolean_config_file_option(option_name="merge", dest="merge")
+    branch_group.add_config_file_option(option_name="merge-mode", dest="merge_mode")
+
+    tag_group.add_boolean_config_file_option(option_name="sign-tags",
+                                             dest="sign_tags")
+    tag_group.add_config_file_option(option_name="keyid",
+                                     dest="keyid")
+    tag_group.add_config_file_option(option_name="upstream-tag",
+                                     dest="upstream_tag")
+    import_group.add_config_file_option(option_name="filter",
+                                        dest="filters", action="append")
+    import_group.add_boolean_config_file_option(option_name="pristine-tar",
+                                                dest="pristine_tar")
+    import_group.add_boolean_config_file_option(option_name="filter-pristine-tar",
+                                                dest="filter_pristine_tar")
+    import_group.add_config_file_option(option_name="import-msg",
+                                        dest="import_msg")
+    import_group.add_boolean_config_file_option(option_name="symlink-orig",
+                                                dest="symlink_orig")
+    import_group.add_config_file_option("component", action="append", metavar='COMPONENT',
+                                        dest="components")
+    cmd_group.add_config_file_option(option_name="postimport", dest="postimport")
+
+    parser.add_boolean_config_file_option(option_name="interactive",
+                                          dest='interactive')
+    parser.add_boolean_config_file_option(option_name="rollback",
+                                          dest="rollback")
+    parser.add_option("-v", "--verbose", action="store_true", dest="verbose", default=False,
+                      help="verbose command execution")
+    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("--uscan", dest='uscan', action="store_true",
+                      default=False, help="use uscan(1) to download the new tarball.")
+
+    # Accepted for compatibility
+    parser.add_option("--download", dest='download', action="store_true",
+                      default=False, help="Ignored. Accepted for compatibility.")
+    return parser
+
+
+def parse_args(argv):
+    """Parse the command line arguments
+    @return: options and arguments
+    """
+
+    parser = build_parser(argv[0])
+    if not parser:
+        return None, None
+
+    (options, args) = parser.parse_args(argv[1:])
+    gbp.log.setup(options.color, options.verbose, options.color_scheme)
+
+    if options.download:
+        gbp.log.warn("Passing --download explicitly is deprecated.")
+
+    options.download = is_download(args)
+    return options, args
+
+
+def main(argv):
+    ret = 0
+    tmpdir = None
+    pristine_orig = None
+    linked = False
+    repo = None
+
+    (options, args) = parse_args(argv)
+    if not options:
+        return ExitCodes.parse_error
+
+    try:
+        try:
+            repo = ImportOrigDebianGitRepository('.')
+        except GitRepositoryError:
+            raise GbpError("%s is not a git repository" % (os.path.abspath('.')))
+
+        is_empty = repo.is_empty()
+
+        if not repo.has_branch(options.upstream_branch) and not is_empty:
+            raise GbpError(no_upstream_branch_msg % options.upstream_branch)
+
+        (clean, out) = repo.is_clean()
+        if not clean and not is_empty:
+            gbp.log.err("Repository has uncommitted changes, commit these first: ")
+            raise GbpError(out)
+
+        # Download the main tarball
+        if options.download:
+            upstream = download_orig(args[0])
+        else:
+            upstream = find_upstream(options.uscan, args)
+            if not upstream:
+                return ExitCodes.uscan_up_to_date
+
+        # The main tarball
+        (sourcepackage, version) = detect_name_and_version(repo, upstream, options)
+        # Additional tarballs we expect to exist
+        component_tarballs = get_component_tarballs(sourcepackage,
+                                                    version,
+                                                    upstream.path,
+                                                    options.components)
+
+        tag = repo.version_to_tag(options.upstream_tag, version)
+        if repo.has_tag(tag):
+            raise GbpError("Upstream tag '%s' already exists" % tag)
+
+        if repo.bare:
+            set_bare_repo_options(options)
+
+        upstream, tmpdir = unpack_tarballs(sourcepackage, upstream, version, component_tarballs, options)
+
+        (pristine_orig, linked) = prepare_pristine_tar(upstream.path,
+                                                       sourcepackage,
+                                                       version)
+
+        # Don't mess up our repo with git metadata from an upstream tarball
+        try:
+            if os.path.isdir(os.path.join(upstream.unpacked, '.git/')):
+                raise GbpError("The orig tarball contains .git metadata - giving up.")
+        except OSError:
+            pass
+
+        try:
+            import_branch = options.upstream_branch
+            filter_msg = ["", " (filtering out %s)"
+                              % options.filters][len(options.filters) > 0]
+            gbp.log.info("Importing '%s' to branch '%s'%s..." % (upstream.path,
+                                                                 import_branch,
+                                                                 filter_msg))
+            gbp.log.info("Source package is %s" % sourcepackage)
+            gbp.log.info("Upstream version is %s" % version)
+
+            msg = upstream_import_commit_msg(options, version)
+
+            commit = repo.commit_dir(upstream.unpacked,
+                                     msg=msg,
+                                     branch=import_branch,
+                                     other_parents=repo.vcs_tag_parent(options.vcs_tag, version),
+                                     create_missing_branch=is_empty,
+                                     )
+
+            if options.pristine_tar:
+                if pristine_orig:
+                    repo.rrr_branch('pristine-tar')
+                    repo.create_pristine_tar_commits(import_branch,
+                                                     pristine_orig,
+                                                     component_tarballs)
+                else:
+                    gbp.log.warn("'%s' not an archive, skipping pristine-tar" % upstream.path)
+
+            repo.create_tag(name=tag,
+                            msg="Upstream version %s" % version,
+                            commit=commit,
+                            sign=options.sign_tags,
+                            keyid=options.keyid)
+
+            if is_empty:
+                repo.create_branch(branch=options.debian_branch, rev=commit)
+                repo.force_head(options.debian_branch, hard=True)
+                # In an empty repo avoid master branch defaulted to by
+                # git and check out debian branch instead.
+                if not repo.bare:
+                    cur = repo.branch
+                    if cur != options.debian_branch:
+                        repo.set_branch(options.debian_branch)
+                        repo.delete_branch(cur)
+            elif options.merge:
+                repo.rrr_branch(options.debian_branch)
+                debian_branch_merge(repo, tag, version, options)
+
+            # Update working copy and index if we've possibly updated the
+            # checked out branch
+            current_branch = repo.get_branch()
+            if current_branch in [options.upstream_branch,
+                                  repo.pristine_tar_branch]:
+                repo.force_head(current_branch, hard=True)
+
+            postimport_hook(repo, tag, version, options)
+        except (gbpc.CommandExecFailed, GitRepositoryError) as err:
+            msg = str(err) or 'Unknown error, please report a bug'
+            raise GbpError("Import of %s failed: %s" % (upstream.path, msg))
+        except KeyboardInterrupt:
+            raise GbpError("Import of %s failed: aborted by user" % (upstream.path))
+    except GbpError as err:
+        if str(err):
+            gbp.log.err(err)
+        ret = 1
+        rollback(repo, options)
+
+    if pristine_orig and linked and not options.symlink_orig:
+        os.unlink(pristine_orig)
+
+    if tmpdir:
+        cleanup_tmp_tree(tmpdir)
+
+    if not ret:
+        gbp.log.info("Successfully imported version %s of %s" % (version, upstream.path))
+    return ret
+
+
+if __name__ == "__main__":
+    sys.exit(main(sys.argv))
+
+# vim:et:ts=4:sw=4:et:sts=4:ai:set list listchars=tab\:»·,trail\:·:
-- 
2.15.1

From a073f0e98bf2ee96cab0fc8f614097aecc4c585c Mon Sep 17 00:00:00 2001
From: Michael Stapelberg <[email protected]>
Date: Fri, 26 Jan 2018 22:38:04 +0100
Subject: [PATCH 2/2] implement gbp import-git subcommand

---
 debian/git-buildpackage.install |  1 +
 gbp/config.py                   |  3 ++
 gbp/scripts/import_git.py       | 91 ++++++++---------------------------------
 3 files changed, 22 insertions(+), 73 deletions(-)

diff --git a/debian/git-buildpackage.install b/debian/git-buildpackage.install
index 4ed4cc7..18fdae6 100644
--- a/debian/git-buildpackage.install
+++ b/debian/git-buildpackage.install
@@ -23,6 +23,7 @@ usr/lib/python3.?/dist-packages/gbp/scripts/dch.py usr/lib/python3/dist-packages
 usr/lib/python3.?/dist-packages/gbp/scripts/import_dsc.py usr/lib/python3/dist-packages/gbp/scripts/
 usr/lib/python3.?/dist-packages/gbp/scripts/import_dscs.py usr/lib/python3/dist-packages/gbp/scripts/
 usr/lib/python3.?/dist-packages/gbp/scripts/import_orig.py usr/lib/python3/dist-packages/gbp/scripts/
+usr/lib/python3.?/dist-packages/gbp/scripts/import_git.py usr/lib/python3/dist-packages/gbp/scripts/
 usr/lib/python3.?/dist-packages/gbp/scripts/export_orig.py usr/lib/python3/dist-packages/gbp/scripts/
 usr/lib/python3.?/dist-packages/gbp/scripts/__init__.py usr/lib/python3/dist-packages/gbp/scripts/
 usr/lib/python3.?/dist-packages/gbp/scripts/pq.py usr/lib/python3/dist-packages/gbp/scripts/
diff --git a/gbp/config.py b/gbp/config.py
index 08293d3..254519b 100644
--- a/gbp/config.py
+++ b/gbp/config.py
@@ -131,6 +131,7 @@ class GbpOptionParser(OptionParser):
                 'full': 'False',
                 'git-author': 'False',
                 'git-log': '--no-merges',
+                'git-ref': 'upstream/latest',
                 'hooks': 'True',
                 'id-length': '0',
                 'ignore-branch': 'False',
@@ -230,6 +231,8 @@ class GbpOptionParser(OptionParser):
             "Files to filter out during import (can be given multiple times), default is %(filter)s",
         'git-author':
             "Use name and email from git-config for changelog trailer, default is '%(git-author)s'",
+        'git-ref':
+            "TODO",
         'full':
             "Include the full commit message instead of only the first line, default is '%(full)s'",
         'meta':
diff --git a/gbp/scripts/import_git.py b/gbp/scripts/import_git.py
index 97b6d54..e05573e 100644
--- a/gbp/scripts/import_git.py
+++ b/gbp/scripts/import_git.py
@@ -34,7 +34,7 @@ from gbp.errors import GbpError
 from gbp.format import format_str
 from gbp.git.vfs import GitVfs
 import gbp.log
-from gbp.scripts.common import ExitCodes, is_download, get_component_tarballs
+from gbp.scripts.common import ExitCodes, get_component_tarballs
 from gbp.scripts.common.import_orig import (orig_needs_repack, cleanup_tmp_tree,
                                             ask_package_name, ask_package_version,
                                             repack_upstream, is_link_target, download_orig)
@@ -471,6 +471,9 @@ def build_parser(name):
                                      dest="keyid")
     tag_group.add_config_file_option(option_name="upstream-tag",
                                      dest="upstream_tag")
+    import_group.add_config_file_option(option_name="git-ref",
+                                        dest="git_ref")
+    import_group.add_option("-V", "--git-version", dest="git_version", help="TODO")
     import_group.add_config_file_option(option_name="filter",
                                         dest="filters", action="append")
     import_group.add_boolean_config_file_option(option_name="pristine-tar",
@@ -494,12 +497,7 @@ def build_parser(name):
     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("--uscan", dest='uscan', action="store_true",
-                      default=False, help="use uscan(1) to download the new tarball.")
 
-    # Accepted for compatibility
-    parser.add_option("--download", dest='download', action="store_true",
-                      default=False, help="Ignored. Accepted for compatibility.")
     return parser
 
 
@@ -515,10 +513,6 @@ def parse_args(argv):
     (options, args) = parser.parse_args(argv[1:])
     gbp.log.setup(options.color, options.verbose, options.color_scheme)
 
-    if options.download:
-        gbp.log.warn("Passing --download explicitly is deprecated.")
-
-    options.download = is_download(args)
     return options, args
 
 
@@ -533,6 +527,9 @@ def main(argv):
     if not options:
         return ExitCodes.parse_error
 
+    # TODO: test --filter option
+    # TODO: add --filter-with-copyright which takes d/copyright into account
+    
     try:
         try:
             repo = ImportOrigDebianGitRepository('.')
@@ -541,83 +538,34 @@ def main(argv):
 
         is_empty = repo.is_empty()
 
-        if not repo.has_branch(options.upstream_branch) and not is_empty:
-            raise GbpError(no_upstream_branch_msg % options.upstream_branch)
-
         (clean, out) = repo.is_clean()
         if not clean and not is_empty:
             gbp.log.err("Repository has uncommitted changes, commit these first: ")
             raise GbpError(out)
 
-        # Download the main tarball
-        if options.download:
-            upstream = download_orig(args[0])
-        else:
-            upstream = find_upstream(options.uscan, args)
-            if not upstream:
-                return ExitCodes.uscan_up_to_date
-
-        # The main tarball
-        (sourcepackage, version) = detect_name_and_version(repo, upstream, options)
-        # Additional tarballs we expect to exist
-        component_tarballs = get_component_tarballs(sourcepackage,
-                                                    version,
-                                                    upstream.path,
-                                                    options.components)
+        version = options.git_version
 
         tag = repo.version_to_tag(options.upstream_tag, version)
-        if repo.has_tag(tag):
-            raise GbpError("Upstream tag '%s' already exists" % tag)
 
         if repo.bare:
             set_bare_repo_options(options)
 
-        upstream, tmpdir = unpack_tarballs(sourcepackage, upstream, version, component_tarballs, options)
-
-        (pristine_orig, linked) = prepare_pristine_tar(upstream.path,
-                                                       sourcepackage,
-                                                       version)
-
-        # Don't mess up our repo with git metadata from an upstream tarball
-        try:
-            if os.path.isdir(os.path.join(upstream.unpacked, '.git/')):
-                raise GbpError("The orig tarball contains .git metadata - giving up.")
-        except OSError:
-            pass
+        commit = repo.get_commits(since=options.git_ref+'~1', until=options.git_ref)[0]
 
         try:
-            import_branch = options.upstream_branch
             filter_msg = ["", " (filtering out %s)"
                               % options.filters][len(options.filters) > 0]
-            gbp.log.info("Importing '%s' to branch '%s'%s..." % (upstream.path,
-                                                                 import_branch,
+            gbp.log.info("Importing '%s' %s..." % (options.git_ref,
                                                                  filter_msg))
-            gbp.log.info("Source package is %s" % sourcepackage)
+            #gbp.log.info("Source package is %s" % sourcepackage)
             gbp.log.info("Upstream version is %s" % version)
 
-            msg = upstream_import_commit_msg(options, version)
-
-            commit = repo.commit_dir(upstream.unpacked,
-                                     msg=msg,
-                                     branch=import_branch,
-                                     other_parents=repo.vcs_tag_parent(options.vcs_tag, version),
-                                     create_missing_branch=is_empty,
-                                     )
-
-            if options.pristine_tar:
-                if pristine_orig:
-                    repo.rrr_branch('pristine-tar')
-                    repo.create_pristine_tar_commits(import_branch,
-                                                     pristine_orig,
-                                                     component_tarballs)
-                else:
-                    gbp.log.warn("'%s' not an archive, skipping pristine-tar" % upstream.path)
-
-            repo.create_tag(name=tag,
-                            msg="Upstream version %s" % version,
-                            commit=commit,
-                            sign=options.sign_tags,
-                            keyid=options.keyid)
+            if not repo.has_tag(tag):
+                repo.create_tag(name=tag,
+                                msg="Upstream version %s" % version,
+                                commit=commit,
+                                sign=options.sign_tags,
+                                keyid=options.keyid)
 
             if is_empty:
                 repo.create_branch(branch=options.debian_branch, rev=commit)
@@ -655,11 +603,8 @@ def main(argv):
     if pristine_orig and linked and not options.symlink_orig:
         os.unlink(pristine_orig)
 
-    if tmpdir:
-        cleanup_tmp_tree(tmpdir)
-
     if not ret:
-        gbp.log.info("Successfully imported version %s of %s" % (version, upstream.path))
+        gbp.log.info("Successfully imported version %s" % (version))
     return ret
 
 
-- 
2.15.1

Reply via email to