Getting Started with Doltgres and Knex.js

DOLTGRESINTEGRATION
17 min read

Doltgres is the Postgres flavor of Dolt. In case you missed it, Doltgres is now Beta! This means that we expect Doltgres to work with all the tools you use with Postgres, including Knex.js.

Knex.js is a "batteries included" SQL query builder for many SQL databases, including Postgres. It features both traditional node style callbacks as well as a promise interface for cleaner async flow control, a stream interface, full-featured query and schema builders, transaction support (with savepoints), connection pooling and standardized responses between different query clients and dialects.

This blog is inspired by a previous blog about getting Started with Dolt and Knex.js and will also walk through the Getting Started example from the Doltgres README, except in Javascript using Knex.js and Node.

Doltgres + Knex.js

TL;DR

If you don't want to run through this whole tutorial and just want the Javascript code, it is available in this GitHub repository on a branch. You can come with any Doltgres SQL server and specify the connection information in a .env file in the repository root.

You must have Git and Node installed to run the code. This example uses a connection to a Hosted Dolt database, but the code will also work if you run a local Doltgres SQL server. If you are using a local Doltgres SQL server instead of Hosted Dolt you must also install Doltgres.

You can find directions for running the code in the repository README. But essentially all you need to do is:

% npm install
% node index.js

This script will reset the database to its original state every time the script runs. The code shows off table creation, Dolt commits, reading Dolt system tables, rollback using Dolt reset, branching, and merging all using Knex.js.

Create a Hosted Dolt deployment

First, go to the Create Deployment page. Select Doltgres and choose whatever instance you want (we're using our $50 trial instance) and click Create Deployment.

Create deployment

Once the deployment has started, you'll see the connectivity information in the Database tab.

Knex example connectivity information

We then add this information to a .env file in the root of the example code repository.

DB_HOST="dolthub-knex-example-dg.dbs.hosted.doltdb.com"
DB_PORT=5432
DB_USER="postgres"
DB_PASSWORD="xxxxxxxxxxxxxxxxxxx"
DB_NAME="postgres"
DB_SSL_PATH="/isrgrootx1.pem"

You will also need to download the Certificate and add it to the root of the repository. The file name should match the DB_SSL_PATH env.

Connect to the Hosted database

You can follow along with the Javascript code here. There are some small differences between Dolt and Doltgres that you can see from the Knex.js code diff here. In the following sections we'll walk through this file and explain what each query does and why.

The knex module itself is a function which takes a configuration object for Knex, accepting a few parameters. The client parameter is required and determines which client adapter will be used with the library. Doltgres is Postgres-compatible so we will use the "postgres" client.

We use dotenv to load the environment variables we set earlier in the .env file. We will use these to create a connection pool to our Hosted database:

require("dotenv").config();
const knex = require("knex");
const fs = require("fs");

const poolConfig = { min: 0, max: 7 };

const config = {
  host: process.env.DB_HOST,
  port: process.env.DB_PORT,
  user: process.env.DB_USER,
  password: process.env.DB_PASSWORD,
  database: process.env.DB_NAME,

  ssl: process.env.DB_SSL_PATH
    ? { ca: fs.readFileSync(__dirname + process.env.DB_SSL_PATH) }
    : false,
};

const db = knex({
  client: "postgres",
  connection: config,
  pool: poolConfig,
});

Once we've created our Knex instance, we can start running queries. We'll start with a function that wraps the Dolt function active_branch().

printActiveBranch();

async function printActiveBranch() {
  const branch = await db.raw(`SELECT ACTIVE_BRANCH()`);
  console.log("Active branch:", branch.rows[0].active_branch);
}

This prints:

Active branch: main

Notice that this function uses async/await. Promises are the preferred way of managing queries in Knex, as they allow you to return values from a fulfillment handler, which in turn become the value of the promise. The main benefit of promises are the ability to catch thrown errors without crashing the Node app, making your code behave like a .try / .catch / .finally in synchronous code.

Create tables

Now let's create some tables. In this example our database will have three tables: employees, teams, and employees_teams. We can utilize Knex's schema builder method createTable, which creates a new table on the database with a callback function to modify the table's structure.

setupDatabase();

async function setupDatabase() {
  await db.schema.createTable("employees", (table) => {
    table.integer("id").primary();
    table.string("last_name");
    table.string("first_name");
  });
  await db.schema.createTable("teams", (table) => {
    table.integer("id").primary();
    table.string("name");
  });
  await db.schema.createTable("employees_teams", (table) => {
    table.integer("employee_id").references("id").inTable("employees");
    table.integer("team_id").references("id").inTable("teams");
    table.primary(["employee_id", "team_id"]);
  });
}

Unlike Dolt which uses a SHOW TABLE statement to examine our changes, we check the pg_tables table in the pg_catalog schema for Doltgres and print the results.

printTables();

async function printTables() {
  const res = await db.raw(
    "SELECT * FROM pg_tables WHERE schemaname = 'public'"
  );
  const tables = res.rows.map((table) => table.tablename).join(", ");
  console.log("Tables in database:", tables);
}

This outputs:

Tables in database: employees, employees_teams, teams

The employees, teams, and employees_teams tables have been created.

Make a Dolt commit

Next, we'll Dolt commit our new tables. Both Git and SQL have the concept of commits and since Doltgres is a combination of Git and SQL it must support both. This can be confusing. A Dolt commit makes an entry in the commit log for versioning purposes. A SQL transaction commit is required to persist your database writes to disk so other connections can see them.

In order to make a Dolt commit, we use the DOLT_COMMIT() function. Doltgres exposes version control write operations as functions, unlike Dolt which exposes them as procedures. Custom Dolt procedures still work in Doltgres but in Postgres, procedures cannot return results. For that reason, Dolt version control functionality was migrated to functions for Doltgres. So, in Doltgres you SELECT DOLT_COMMIT('-Am', 'new change') to create a commit instead of CALL DOLT_COMMIT('-Am', 'new change') like you would in Dolt.

The naming of these functions follows the Git command line standard. git add on the Git command line becomes DOLT_ADD() as a Doltgres SQL function or Dolt SQL procedure. Arguments mimic Git as well. If you know Git, you already know how to use Doltgres.

Knex.js doesn't have a query builder for functions. We have to use a raw SQL statement using Knex's raw() method. For this method, you can specify a commit author and message, which are passed as arguments into the Doltgres function. Like Git, Dolt has a staging area, so we include a -A option to add all tables before making a commit. The resulting code looks like this:

doltCommit("Taylor <taylor@dolthub.com>", "Created tables");

async function doltCommit(author, msg) {
  const res = await db.raw(
    `SELECT DOLT_COMMIT('--author', ?::text, '-Am', ?::text)`,
    [author, msg]
  );
  console.log("Created commit:", res.rows[0].dolt_commit[0]);
}

And running it results in the output:

Created commit: 80ks3k1ook712vauvavdnnd6t3q86e3d

Examine the commit log

Let's examine the Dolt commit log. Doltgres version control read operations are exposed in SQL as custom system tables or table functions. The commit log can be read using the dolt.log system table, named after the git log and dolt log command line equivalents. System tables in Doltgres look slightly different than system tables in Dolt, mostly due to how schemas work in Postgres. You can read about the background of that migration here. In Doltgres, you access commit logs using the log table in the dolt schema (i.e. SELECT * FROM dolt.log), whereas Dolt does not have schemas so all system tables are prefixed with dolt_ (i.e. SELECT * FROM dolt_log).

We can use Knex's query builder select method to select and order the log elements we want to print. The resulting code looks like this:

printCommitLog();

async function printCommitLog() {
  const res = await db
    .select("commit_hash", "committer", "message")
    .from("dolt.log")
    .orderBy("date", "desc");
  console.log("Commit log:");
  res.forEach((log) =>
    console.log(`  ${log.commit_hash}: ${log.message} by ${log.committer}`)
  );
}

And it outputs:

Commit log:
  80ks3k1ook712vauvavdnnd6t3q86e3d: Created tables by Taylor
  dtn5h4oquovaes0n719b3sagbecbkp1h: Initialize data repository by Dolt System Account

Insert data

Now we're going to populate the tables with some data. We can use the insert method and optionally specify conflict behavior using onConflict().merge() or onConflict().ignore().

insertData();

async function insertData() {
  await db("employees").insert([
    { id: 0, last_name: "Sehn", first_name: "Tim" },
    { id: 1, last_name: "Hendriks", first_name: "Brian" },
    { id: 2, last_name: "Son", first_name: "Aaron" },
    { id: 3, last_name: "Fitzgerald", first_name: "Brian" },
  ]);

  await db("teams").insert([
    { id: 0, name: "Engineering" },
    { id: 1, name: "Sales" },
  ]);

  await db("employees_teams").insert([
    { employee_id: 0, team_id: 0 },
    { employee_id: 1, team_id: 0 },
    { employee_id: 2, team_id: 0 },
    { employee_id: 0, team_id: 1 },
    { employee_id: 3, team_id: 1 },
  ]);
}

We can make sure our inserts worked by displaying a summary table. Knex.js comes with a query builder that supports many types of complex SQL queries. In this example, we'll construct a three table join. Later in this example we'll change the schema, so we'll account for that in this function.

printSummaryTable();

async function printSummaryTable() {
  // Get all employees columns because we change the schema
  const colInfo = await db("employees").columnInfo();
  const employeeCols = Object.keys(colInfo)
    .filter((col) => col !== "id")
    .map((col) => `employees.${col}`);

  // Dolt supports up to 12 table joins. Here we do a 3 table join.
  const res = await db
    .select("teams.name", ...employeeCols)
    .from("employees")
    .join("employees_teams", "employees.id", "employees_teams.employee_id")
    .join("teams", "teams.id", "employees_teams.team_id")
    .orderBy("teams.name", "asc");

  console.log("Summary:");
  res.forEach((row) => {
    let startDate = "";
    if ("start_date" in row) {
      if (row.start_date === null) {
        startDate = "None";
      } else {
        const d = new Date(row.start_date);
        startDate = d.toDateString();
      }
    }
    console.log(
      `  ${row.name}: ${row.first_name} ${row.last_name} ${startDate}`
    );
  });
}

Which results in the following output:

Summary:
  Engineering: Tim Sehn
  Engineering: Brian Hendriks
  Engineering: Aaron Son
  Sales: Tim Sehn
  Sales: Brian Fitzgerald

Examine the status and diff

You can use the dolt.status (dolt_status for Dolt) system table to see what tables changed.

printStatus();

async function printStatus() {
  const res = await db.select("*").from("dolt.status");
  console.log("Status:");
  if (res.length === 0) {
    console.log("  No tables modified");
  } else {
    res.forEach((row) => {
      console.log(`  ${row.table_name}: ${row.status}`);
    });
  }
}

The resulting output looks like:

Status:
  employees_teams: modified
  employees: modified
  teams: modified

Now that I see which tables changed and how, I want to see what rows changed in a particular table. Doltgres is built on Dolt, which is built from the ground up to provide fast diffs between table versions even for very large tables. In Doltgres, there are a few ways to view diffs: a dolt_diff_<table> system table for each user-defined table and a dolt_diff() table function. We filter the diff table down to WORKING changes so we only see changes that aren't staged or committed.

printDiff("employees");

async function printDiff(table) {
  const res = await db
    .select("*")
    .from(`dolt_diff_${table}`)
    .where("to_commit", "WORKING");
  console.log(`Diff for ${table}:`);
  console.log(res);
}

The resulting output looks like:

Diff for employees:
[
  {
    to_id: 0,
    to_last_name: 'Sehn',
    to_first_name: 'Tim',
    to_commit: 'WORKING',
    to_commit_date: null,
    from_id: null,
    from_last_name: null,
    from_first_name: null,
    from_commit: '9iuopee4d7qdkbo8m8vn69j5qq9ee871',
    from_commit_date: 2023-09-20T05:59:42.672Z,
    diff_type: 'added'
  },
  {
    to_id: 1,
    to_last_name: 'Hendriks',
    to_first_name: 'Brian',
    to_commit: 'WORKING',
    to_commit_date: null,
    from_id: null,
    from_last_name: null,
    from_first_name: null,
    from_commit: '9iuopee4d7qdkbo8m8vn69j5qq9ee871',
    from_commit_date: 2023-09-20T05:59:42.672Z,
    diff_type: 'added'
  },
  {
    to_id: 2,
    to_last_name: 'Son',
    to_first_name: 'Aaron',
    to_commit: 'WORKING',
    to_commit_date: null,
    from_id: null,
    from_last_name: null,
    from_first_name: null,
    from_commit: '9iuopee4d7qdkbo8m8vn69j5qq9ee871',
    from_commit_date: 2023-09-20T05:59:42.672Z,
    diff_type: 'added'
  },
  {
    to_id: 3,
    to_last_name: 'Fitzgerald',
    to_first_name: 'Brian',
    to_commit: 'WORKING',
    to_commit_date: null,
    from_id: null,
    from_last_name: null,
    from_first_name: null,
    from_commit: '9iuopee4d7qdkbo8m8vn69j5qq9ee871',
    from_commit_date: 2023-09-20T05:59:42.672Z,
    diff_type: 'added'
  }
]

Before we go onto the next section let's Dolt commit our changes.

await doltCommit("Tim <tim@dolthub.com>", "Inserted data into tables");
await printCommitLog();

And you'll see our new commit in the log:

Created commit: flfiptf13lc7chlvbkikqtss3r02k2se
Commit log:
  flfiptf13lc7chlvbkikqtss3r02k2se: Inserted data into tables by Tim
  80ks3k1ook712vauvavdnnd6t3q86e3d: Created tables by Taylor
  dtn5h4oquovaes0n719b3sagbecbkp1h: Initialize data repository by Dolt System Account

Rolling back a mistake

Doltgres has powerful rollback capabilities. Let's imagine I accidentally drop a table. The foreign keys will prevent me from dropping employees or teams, but employees_teams is not safe from an accident.

dropTable("employees_teams");

async function dropTable(table) {
  await db.schema.dropTable(table);
}

As we can see from status and SHOW TABLES it is gone.

await printStatus();
await printTables();
Status:
  employees_teams: deleted
Tables in database: employees, teams

In a traditional database, this could be disastrous. In Doltgres, we can get it back with a simple select dolt_reset('hard'). This function takes an optional commit. If no commit is specified it resets to the HEAD commit.

await doltResetHard();
await printStatus();
await printTables();

async function doltResetHard(commit) {
  if (commit) {
    await db.raw(`SELECT DOLT_RESET('--hard', ?::text)`, [commit]);
    console.log("Resetting to commit:", commit);
  } else {
    await db.raw(`SELECT DOLT_RESET('--hard')`);
    console.log("Resetting to HEAD");
  }
}
Resetting to HEAD
Status:
  No tables modified
Tables in database: employees, employees_teams, teams

Doltgres makes operating databases less error-prone. You can always back out changes you have in progress or rewind to a known good state.

Change data on a branch

Dolt and Doltgres are the only SQL databases with branches and merges. We will create and switch branches, and then make some changes and commit them to this new branch. Later, we'll merge all the changes together. Think of a branch as a really long SQL transaction.

First, you need to create a branch. Creating a branch is a write so you do it with a function, dolt_branch(). In the Javascript code, we also consult the dolt.branches system table to make sure the branch does not already exist. Then we use dolt_checkout() to switch branches.

await createBranch("modify_data");
await checkoutBranch("modify_data");

async function getBranch(branch) {
  return db.select("name").from("dolt.branches").where("name", branch);
}

async function createBranch(branch) {
  const res = await getBranch(branch);
  if (res.length > 0) {
    console.log("Branch exists:", branch);
  } else {
    await db.raw(`SELECT DOLT_BRANCH(?::text)`, [branch]);
    console.log("Created branch:", branch);
  }
}

async function checkoutBranch(branch) {
  await db.raw(`SELECT DOLT_CHECKOUT(?::text)`, [branch]);
  console.log("Using branch:", branch);
}

Now that we're on a new branch, it's safe to make changes and the main branch will remain unchanged. We are going to use a transaction to insert, update, and delete using the Knex query builder. All queries within a transaction are executed on the same database connection, and run the entire set of queries as a single unit of work. Any failure will mean the database will rollback any queries executed on that connection to the pre-transaction state.

modifyData();

async function modifyData() {
  try {
    await db.transaction(async (trx) => {
      await trx("employees")
        .where("first_name", "Tim")
        .update("first_name", "Timothy");

      await trx("employees").insert({
        id: 4,
        last_name: "Bantle",
        first_name: "Taylor",
      });

      await trx("employees_teams").insert({
        employee_id: 4,
        team_id: 0,
      });

      await trx("employees_teams")
        .where("employee_id", 0)
        .where("employee_id", 1)
        .del();
    });
  } catch (err) {
    // Rolls back transaction
    console.error(err);
  }
}

Let's inspect what we've done to make sure it looks right.

await printStatus();
await printDiff("employees");
await printDiff("employees_teams");
await printSummaryTable();
Status:
  employees: modified
  employees_teams: modified
Diff for employees:
[
  {
    to_id: 0,
    to_last_name: 'Sehn',
    to_first_name: 'Timothy',
    to_commit: 'WORKING',
    to_commit_date: null,
    from_id: 0,
    from_last_name: 'Sehn',
    from_first_name: 'Tim',
    from_commit: 'bu68epe1k4el46l9cfsdishuf5665q7m',
    from_commit_date: 2023-09-20T06:20:43.351Z,
    diff_type: 'modified'
  },
  {
    to_id: 4,
    to_last_name: 'Bantle',
    to_first_name: 'Taylor',
    to_commit: 'WORKING',
    to_commit_date: null,
    from_id: null,
    from_last_name: null,
    from_first_name: null,
    from_commit: 'bu68epe1k4el46l9cfsdishuf5665q7m',
    from_commit_date: 2023-09-20T06:20:43.351Z,
    diff_type: 'added'
  }
]
Diff for employees_teams:
[
  {
    to_employee_id: 4,
    to_team_id: 0,
    to_commit: 'WORKING',
    to_commit_date: null,
    from_employee_id: null,
    from_team_id: null,
    from_commit: 'bu68epe1k4el46l9cfsdishuf5665q7m',
    from_commit_date: 2023-09-20T06:20:43.351Z,
    diff_type: 'added'
  }
]
Summary:
  Engineering: Brian Hendriks
  Engineering: Aaron Son
  Engineering: Taylor Bantle
  Sales: Timothy Sehn
  Sales: Brian Fitzgerald

I am added to the engineering team on the modify_data branch. Tim is no longer on the Sales team.

Finally, let's commit these changes so we can make different changes on another branch.

await doltCommit("Brian <brian@dolthub.com>", "Modified data on branch");
await printCommitLog();
Created commit: j9taft0oie0kg1rhss4glvvusv0tdao9
Commit log:
  j9taft0oie0kg1rhss4glvvusv0tdao9: Modified data on branch by Brian
  flfiptf13lc7chlvbkikqtss3r02k2se: Inserted data into tables by Tim
  80ks3k1ook712vauvavdnnd6t3q86e3d: Created tables by Taylor
  dtn5h4oquovaes0n719b3sagbecbkp1h: Initialize data repository by Dolt System Account

Change schema on another branch

We're going to make a schema change on another branch and make some data modifications. Below, you'll see we check out the main branch so the new branch has the correct base branch. Then, we create a new branch called modify_schema and run the modify_schema() function, which uses a transaction to add a start date column and populate it. We finally use status and diff to show off what changed.

await checkoutBranch("main");
await createBranch("modify_schema");
await checkoutBranch("modify_schema");
await printActiveBranch();
await modifySchema();
await printStatus();
await printDiff("employees");
await printSummaryTable();

async function modifySchema() {
  try {
    await db.transaction(async (trx) => {
      await trx.schema.alterTable("employees", (table) => {
        table.date("start_date");
      });

      await trx("employees").where("id", 0).update("start_date", "2018-08-06");
      await trx("employees").where("id", 1).update("start_date", "2018-08-06");
      await trx("employees").where("id", 2).update("start_date", "2018-08-06");
      await trx("employees").where("id", 3).update("start_date", "2021-04-19");
    });
  } catch (err) {
    // Rolls back transaction
    console.error(err);
  }
}

This outputs the following:

Using branch: main
Created branch: modify_schema
Using branch: modify_schema
Active branch: modify_schema
Status:
  employees: modified
Diff for employees:
[
  {
    to_id: 0,
    to_last_name: 'Sehn',
    to_first_name: 'Tim',
    to_start_date: 2018-08-06T07:00:00.000Z,
    to_commit: 'WORKING',
    to_commit_date: null,
    from_id: 0,
    from_last_name: 'Sehn',
    from_first_name: 'Tim',
    from_start_date: null,
    from_commit: '3li6nho3o9891ersocjh4q3l371uj880',
    from_commit_date: 2023-09-20T06:28:42.459Z,
    diff_type: 'modified'
  },
  {
    to_id: 1,
    to_last_name: 'Hendriks',
    to_first_name: 'Brian',
    to_start_date: 2018-08-06T07:00:00.000Z,
    to_commit: 'WORKING',
    to_commit_date: null,
    from_id: 1,
    from_last_name: 'Hendriks',
    from_first_name: 'Brian',
    from_start_date: null,
    from_commit: '3li6nho3o9891ersocjh4q3l371uj880',
    from_commit_date: 2023-09-20T06:28:42.459Z,
    diff_type: 'modified'
  },
  {
    to_id: 2,
    to_last_name: 'Son',
    to_first_name: 'Aaron',
    to_start_date: 2018-08-06T07:00:00.000Z,
    to_commit: 'WORKING',
    to_commit_date: null,
    from_id: 2,
    from_last_name: 'Son',
    from_first_name: 'Aaron',
    from_start_date: null,
    from_commit: '3li6nho3o9891ersocjh4q3l371uj880',
    from_commit_date: 2023-09-20T06:28:42.459Z,
    diff_type: 'modified'
  },
  {
    to_id: 3,
    to_last_name: 'Fitzgerald',
    to_first_name: 'Brian',
    to_start_date: 2021-04-19T07:00:00.000Z,
    to_commit: 'WORKING',
    to_commit_date: null,
    from_id: 3,
    from_last_name: 'Fitzgerald',
    from_first_name: 'Brian',
    from_start_date: null,
    from_commit: '3li6nho3o9891ersocjh4q3l371uj880',
    from_commit_date: 2023-09-20T06:28:42.459Z,
    diff_type: 'modified'
  }
]
Summary:
  Engineering: Brian Hendriks Mon Aug 06 2018
  Engineering: Aaron Son Mon Aug 06 2018
  Sales: Tim Sehn Mon Aug 06 2018
  Sales: Brian Fitzgerald Mon Apr 19 2021

As you can see our defensive coding in the Insert data section paid off and employee start dates are displayed. This looks good so we'll commit it.

await doltCommit("Taylor <taylor@dolthub.com>", "Modified schema on branch");
await printCommitLog();
Created commit: 4j10oomkf2vp1309eoo01bq4ha4t6iq2
Commit log:
  4j10oomkf2vp1309eoo01bq4ha4t6iq2: Modified schema on branch by Taylor
  flfiptf13lc7chlvbkikqtss3r02k2se: Inserted data into tables by Tim
  80ks3k1ook712vauvavdnnd6t3q86e3d: Created tables by Taylor
  dtn5h4oquovaes0n719b3sagbecbkp1h: Initialize data repository by Dolt System Account

Merge it all together

Now we will merge all the branches together and show the resulting summary table. To merge, use the function dolt_merge().

await checkoutBranch("main");
await printActiveBranch();
await printCommitLog();
await printSummaryTable();
await doltMerge("modify_data");
await printSummaryTable();
await printCommitLog();
await doltMerge("modify_schema");
await printSummaryTable();
await printCommitLog();

async function doltMerge(branch) {
  const res = await db.raw(`SELECT DOLT_MERGE(?::text)`, [branch]);
  console.log("Merge complete for ", branch);
  const mergeRes = res.rows[0].dolt_merge;
  console.log(`  Commit: ${mergeRes[0]}`);
  console.log(`  Fast forward: ${mergeRes[1]}`);
  console.log(`  Conflicts: ${mergeRes[2]}`);
}

This outputs the following. You can see the data and schema evolving as we merge.

Using branch: main
Active branch: main
Commit log:
  flfiptf13lc7chlvbkikqtss3r02k2se: Inserted data into tables by Tim
  80ks3k1ook712vauvavdnnd6t3q86e3d: Created tables by Taylor
  dtn5h4oquovaes0n719b3sagbecbkp1h: Initialize data repository by Dolt System Account
Summary:
  Engineering: Brian Hendriks
  Engineering: Aaron Son
  Sales: Tim Sehn
  Sales: Brian Fitzgerald
Merge complete for  modify_data
  Commit: 9he3pav122o3qmh7vlm58muv0ie2600j
  Fast forward: 1
  Conflicts: 0
Summary:
  Engineering: Brian Hendriks
  Engineering: Aaron Son
  Engineering: Taylor Bantle
  Sales: Timothy Sehn
  Sales: Brian Fitzgerald
Commit log:
  j9taft0oie0kg1rhss4glvvusv0tdao9: Modified data on branch by Brian
  flfiptf13lc7chlvbkikqtss3r02k2se: Inserted data into tables by Tim
  80ks3k1ook712vauvavdnnd6t3q86e3d: Created tables by Taylor
  dtn5h4oquovaes0n719b3sagbecbkp1h: Initialize data repository by Dolt System Account
Merge complete for  modify_schema
  Commit: fjfuj3h9nuap933qtt0aa4fske2h8r6b
  Fast forward: 0
  Conflicts: 0
Summary:
  Engineering: Brian Hendriks Mon Aug 06 2018
  Engineering: Aaron Son Mon Aug 06 2018
  Engineering: Taylor Bantle None
  Sales: Timothy Sehn Mon Aug 06 2018
  Sales: Brian Fitzgerald Mon Apr 19 2021
Commit log:
  fjfuj3h9nuap933qtt0aa4fske2h8r6b: Merge branch 'modify_schema' into main by 14i5lj63h3u8l4kv
  4j10oomkf2vp1309eoo01bq4ha4t6iq2: Modified schema on branch by Taylor
  j9taft0oie0kg1rhss4glvvusv0tdao9: Modified data on branch by Brian
  flfiptf13lc7chlvbkikqtss3r02k2se: Inserted data into tables by Tim
  80ks3k1ook712vauvavdnnd6t3q86e3d: Created tables by Taylor
  dtn5h4oquovaes0n719b3sagbecbkp1h: Initialize data repository by Dolt System Account

Notice the first merge was a fast-forward merge just like in Git. Doltgres will detect schema and data conflicts if you make them.

Conclusion

Congratulations on making it this far! You can now start building your own Doltgres application using Node and Knex.js. If you have any questions or need more help getting starting, stop by our Discord.

SHARE

JOIN THE DATA EVOLUTION

Get started with Dolt

Or join our mailing list to get product updates.