commands: support bridge imports after a given date and resumable imports

amine created

Change summary

bridge/core/bridge.go              | 79 +++++++++++++++++++++++++++++--
commands/bridge_pull.go            | 34 ++++++++++--
commands/bridge_push.go            | 17 ------
doc/man/git-bug-bridge-pull.1      |  8 +++
doc/md/git-bug_bridge_pull.md      |  4 +
misc/bash_completion/git-bug       |  7 ++
misc/powershell_completion/git-bug |  4 +
misc/zsh_completion/git-bug        |  4 +
8 files changed, 125 insertions(+), 32 deletions(-)

Detailed changes

bridge/core/bridge.go 🔗

@@ -7,6 +7,7 @@ import (
 	"reflect"
 	"regexp"
 	"sort"
+	"strconv"
 	"strings"
 	"time"
 
@@ -291,7 +292,7 @@ func (b *Bridge) ensureInit() error {
 	return nil
 }
 
-func (b *Bridge) ImportAll(ctx context.Context, since time.Time) (<-chan ImportResult, error) {
+func (b *Bridge) startImportSince(ctx context.Context, since time.Time) (<-chan ImportResult, error) {
 	importer := b.getImporter()
 	if importer == nil {
 		return nil, ErrImportNotSupported
@@ -307,24 +308,88 @@ func (b *Bridge) ImportAll(ctx context.Context, since time.Time) (<-chan ImportR
 		return nil, err
 	}
 
-	return importer.ImportAll(ctx, b.repo, since)
+	events, err := importer.ImportAll(ctx, b.repo, since)
+	if err != nil {
+		return nil, err
+	}
+
+	return events, nil
+}
+
+func (b *Bridge) ImportAllSince(ctx context.Context, since time.Time) (<-chan ImportResult, error) {
+	if since.Equal(time.Time{}) {
+		lastImportTimeStr, err := b.repo.LocalConfig().ReadString(fmt.Sprintf("git-bug.bridge.%s.lastImportTime", b.Name))
+		if err == nil {
+			lastImportTime, err := strconv.Atoi(lastImportTimeStr)
+			if err != nil {
+				return nil, err
+			}
+			since = time.Unix(int64(lastImportTime), 0)
+		}
+	}
+
+	importStartTime := time.Now().Unix()
+
+	events, err := b.startImportSince(ctx, since)
+	if err != nil {
+		return nil, err
+	}
+
+	out := make(chan ImportResult)
+	go func() {
+		defer close(out)
+
+		for event := range events {
+			out <- event
+		}
+
+		// do not store last import time if context was cancelled
+		if ctx.Err() == nil {
+			err = b.repo.LocalConfig().StoreString(fmt.Sprintf("git-bug.bridge.%s.lastImportTime", b.Name), strconv.Itoa(int(importStartTime)))
+		}
+
+	}()
+
+	return out, nil
 }
 
-func (b *Bridge) ExportAll(ctx context.Context, since time.Time) (<-chan ExportResult, error) {
+func (b *Bridge) ImportAll(ctx context.Context) (<-chan ImportResult, error) {
+	return b.startImportSince(ctx, time.Time{})
+}
+
+func (b *Bridge) ExportAll(ctx context.Context, since time.Time) error {
 	exporter := b.getExporter()
 	if exporter == nil {
-		return nil, ErrExportNotSupported
+		return ErrExportNotSupported
 	}
 
 	err := b.ensureConfig()
 	if err != nil {
-		return nil, err
+		return err
 	}
 
 	err = b.ensureInit()
 	if err != nil {
-		return nil, err
+		return err
+	}
+
+	events, err := exporter.ExportAll(ctx, b.repo, since)
+	if err != nil {
+		return err
 	}
 
-	return exporter.ExportAll(ctx, b.repo, since)
+	exportedIssues := 0
+	for result := range events {
+		if result.Event != ExportEventNothing {
+			fmt.Println(result.String())
+		}
+
+		switch result.Event {
+		case ExportEventBug:
+			exportedIssues++
+		}
+	}
+
+	fmt.Printf("exported %d issues with %s bridge\n", exportedIssues, b.Name)
+	return nil
 }

commands/bridge_pull.go 🔗

@@ -15,7 +15,16 @@ import (
 	"github.com/MichaelMure/git-bug/util/interrupt"
 )
 
+var (
+	importSince int64
+	noResume    bool
+)
+
 func runBridgePull(cmd *cobra.Command, args []string) error {
+	if noResume && importSince != 0 {
+		return fmt.Errorf("only one of --no-resume and --since flags should be used")
+	}
+
 	backend, err := cache.NewRepoCache(repo)
 	if err != nil {
 		return err
@@ -64,10 +73,19 @@ func runBridgePull(cmd *cobra.Command, args []string) error {
 		return nil
 	})
 
-	// TODO: by default import only new events
-	events, err := b.ImportAll(ctx, time.Time{})
-	if err != nil {
-		return err
+	var events <-chan core.ImportResult
+	if noResume {
+		events, err = b.ImportAll(ctx)
+	} else {
+		var since time.Time
+		if importSince != 0 {
+			since = time.Unix(importSince, 0)
+		}
+
+		events, err = b.ImportAllSince(ctx, since)
+		if err != nil {
+			return err
+		}
 	}
 
 	importedIssues := 0
@@ -85,12 +103,12 @@ func runBridgePull(cmd *cobra.Command, args []string) error {
 		}
 	}
 
+	fmt.Printf("imported %d issues and %d identities with %s bridge\n", importedIssues, importedIdentities, b.Name)
+
 	// send done signal
 	close(done)
 
-	fmt.Printf("Successfully imported %d issues and %d identities with %s bridge\n", importedIssues, importedIdentities, b.Name)
-
-	return nil
+	return err
 }
 
 var bridgePullCmd = &cobra.Command{
@@ -103,4 +121,6 @@ var bridgePullCmd = &cobra.Command{
 
 func init() {
 	bridgeCmd.AddCommand(bridgePullCmd)
+	bridgePullCmd.Flags().BoolVarP(&noResume, "no-resume", "n", false, "force importing all bugs")
+	bridgePullCmd.Flags().Int64VarP(&importSince, "since", "s", 0, "import only bugs updated after the given date (must be a unix timestamp)")
 }

commands/bridge_push.go 🔗

@@ -63,28 +63,13 @@ func runBridgePush(cmd *cobra.Command, args []string) error {
 		return nil
 	})
 
-	// TODO: by default export only new events
-	events, err := b.ExportAll(ctx, time.Time{})
+	err = b.ExportAll(ctx, time.Time{})
 	if err != nil {
 		return err
 	}
 
-	exportedIssues := 0
-	for result := range events {
-		if result.Event != core.ExportEventNothing {
-			fmt.Println(result.String())
-		}
-
-		switch result.Event {
-		case core.ExportEventBug:
-			exportedIssues++
-		}
-	}
-
 	// send done signal
 	close(done)
-
-	fmt.Printf("Successfully exported %d issues with %s bridge\n", exportedIssues, b.Name)
 	return nil
 }
 

doc/man/git-bug-bridge-pull.1 🔗

@@ -23,6 +23,14 @@ Pull updates.
 \fB\-h\fP, \fB\-\-help\fP[=false]
     help for pull
 
+.PP
+\fB\-n\fP, \fB\-\-no\-resume\fP[=false]
+    force importing all bugs
+
+.PP
+\fB\-s\fP, \fB\-\-since\fP=0
+    import only bugs updated after the given date (must be a unix timestamp)
+
 
 .SH SEE ALSO
 .PP

doc/md/git-bug_bridge_pull.md 🔗

@@ -13,7 +13,9 @@ git-bug bridge pull [<name>] [flags]
 ### Options
 
 ```
-  -h, --help   help for pull
+  -h, --help        help for pull
+  -n, --no-resume   force importing all bugs
+  -s, --since int   import only bugs updated after the given date (must be a unix timestamp)
 ```
 
 ### SEE ALSO

misc/bash_completion/git-bug 🔗

@@ -347,6 +347,13 @@ _git-bug_bridge_pull()
     flags_with_completion=()
     flags_completion=()
 
+    flags+=("--no-resume")
+    flags+=("-n")
+    local_nonpersistent_flags+=("--no-resume")
+    flags+=("--since=")
+    two_word_flags+=("--since")
+    two_word_flags+=("-s")
+    local_nonpersistent_flags+=("--since=")
 
     must_have_one_flag=()
     must_have_one_noun=()

misc/powershell_completion/git-bug 🔗

@@ -71,6 +71,10 @@ Register-ArgumentCompleter -Native -CommandName 'git-bug' -ScriptBlock {
             break
         }
         'git-bug;bridge;pull' {
+            [CompletionResult]::new('-n', 'n', [CompletionResultType]::ParameterName, 'force importing all bugs')
+            [CompletionResult]::new('--no-resume', 'no-resume', [CompletionResultType]::ParameterName, 'force importing all bugs')
+            [CompletionResult]::new('-s', 's', [CompletionResultType]::ParameterName, 'import only bugs updated after the given date (must be a unix timestamp)')
+            [CompletionResult]::new('--since', 'since', [CompletionResultType]::ParameterName, 'import only bugs updated after the given date (must be a unix timestamp)')
             break
         }
         'git-bug;bridge;push' {

misc/zsh_completion/git-bug 🔗

@@ -151,7 +151,9 @@ function _git-bug_bridge_configure {
 }
 
 function _git-bug_bridge_pull {
-  _arguments
+  _arguments \
+    '(-n --no-resume)'{-n,--no-resume}'[force importing all bugs]' \
+    '(-s --since)'{-s,--since}'[import only bugs updated after the given date (must be a unix timestamp)]:'
 }
 
 function _git-bug_bridge_push {