Skip to content

Commit 4d5c55f

Browse files
authored
Add turso db branch command (#1024)
The `turso db create --from-db` syntax is awkward and not discoverable. Let's add a `turso db branch` command for better DX (but keep the create variant too).
2 parents b296e1b + a313d55 commit 4d5c55f

2 files changed

Lines changed: 87 additions & 0 deletions

File tree

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,12 @@ Or you can specify the name of the database with:
8585
turso db create <database name>
8686
```
8787

88+
To create a branch from an existing database, run:
89+
90+
```bash
91+
turso db branch <source-database> <target-database>
92+
```
93+
8894
### Start SQL shell
8995

9096
You can start an interactive SQL shell similar to `sqlite3` with:

internal/cmd/db_branch.go

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package cmd
2+
3+
import (
4+
"errors"
5+
"strings"
6+
7+
"github.com/spf13/cobra"
8+
)
9+
10+
var branchTimestampFlag string
11+
12+
func init() {
13+
dbCmd.AddCommand(dbBranchCmd)
14+
addGroupFlag(dbBranchCmd)
15+
addLocationFlag(dbBranchCmd, "Location ID. If no ID is specified, primary location of the destination group is used by default.")
16+
addRemoteEncryptionCipherFlag(dbBranchCmd)
17+
addRemoteEncryptionKeyFlag(dbBranchCmd)
18+
dbBranchCmd.Flags().StringVar(&branchTimestampFlag, "timestamp", "", "Set a point in time in the past to copy data from the source database. Must be in RFC3339 format like '2023-09-29T10:16:13-03:00'")
19+
}
20+
21+
var dbBranchCmd = &cobra.Command{
22+
Use: "branch <source-database> <target-database>",
23+
Short: "Create a branch from an existing database.",
24+
Args: cobra.ExactArgs(2),
25+
ValidArgsFunction: dbBranchArgs,
26+
RunE: func(cmd *cobra.Command, args []string) error {
27+
cmd.SilenceUsage = true
28+
sourceName := args[0]
29+
30+
client, err := authedTursoClient()
31+
if err != nil {
32+
return err
33+
}
34+
sourceDB, err := getDatabase(client, sourceName, true)
35+
if err != nil {
36+
return err
37+
}
38+
39+
targetName, err := resolveBranchTargetName(args)
40+
if err != nil {
41+
return err
42+
}
43+
44+
prevFromDB := fromDBFlag
45+
prevTimestamp := timestampFlag
46+
prevGroup := groupFlag
47+
defer func() {
48+
fromDBFlag = prevFromDB
49+
timestampFlag = prevTimestamp
50+
groupFlag = prevGroup
51+
}()
52+
53+
fromDBFlag = sourceName
54+
timestampFlag = branchTimestampFlag
55+
if groupFlag == "" && sourceDB.Group != "" {
56+
groupFlag = sourceDB.Group
57+
}
58+
59+
return CreateDatabase(targetName)
60+
},
61+
}
62+
63+
func dbBranchArgs(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
64+
if len(args) == 0 {
65+
return dbNameArg(cmd, args, toComplete)
66+
}
67+
return noFilesArg(cmd, args, toComplete)
68+
}
69+
70+
func resolveBranchTargetName(args []string) (string, error) {
71+
targetName := args[1]
72+
73+
targetName = strings.TrimSpace(targetName)
74+
if targetName == "" {
75+
return "", errors.New("branch name cannot be empty")
76+
}
77+
if targetName == args[0] {
78+
return "", errors.New("branch name must be different from source database name")
79+
}
80+
return targetName, nil
81+
}

0 commit comments

Comments
 (0)