Skip to content

Instantly share code, notes, and snippets.

@squarism
Created December 5, 2023 19:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save squarism/b3b80185034a1b8798259e69fc855907 to your computer and use it in GitHub Desktop.
Save squarism/b3b80185034a1b8798259e69fc855907 to your computer and use it in GitHub Desktop.
lockrebase Demo

Creating a Merge Conflict on Purpose

For this demo, we will be using node/javascript and NPM, only because it is sort of a lingua franca.

Let's create a merge conflict. It's pretty easy to do.

  1. Fork a branch, add a dependency.
  2. On the main branch, add another dependency.
  3. Now the poor person on the feature branch will get a merge conflict on the lock file when they "catch up on main".

Here are the complete steps:

# just pick a place to play in
cd /tmp

# certain languages / package managers will create a directory for you, NPM does not
mkdir conflict
cd conflict

# init project with npm
npm init --yes

# create git repo
git init .

# create an empty lock file
npm install

# git status will show you this
# package-lock.json  <- the lock file for machines
# package.json       <- the declarative dependencies file for humans

# add a .gitignore
echo "node_modules" > .gitignore

# add what we have for main
git add .
git commit -m "Initial commit"

This creates an empty main branch. Now to create the conflict.

# start a feature branch
git checkout -b add_axios
npm add axios
git add .
git commit -m "Add axios"

This is the feature branch that will conflict after main changes from underneath it.

# change main from underneath `add_axios`
git checkout main

# ;)
npm add left-pad

# npm WARN deprecated left-pad@1.3.0: use String.prototype.padStart()
# added 1 package, removed 9 packages, and audited 2 packages in 684ms

git add .
git commit -m "Add left-pad"

Glad to see they print a warning on left-pad. ;)

Now, when we switch back to add_axios and catch up, two files will conflict:

  1. package.json - the file that a human can edit and read
  2. package-lock.json - the file that humans should not read
git checkout add_axios
git rebase main

We will get this error.

Auto-merging package-lock.json
CONFLICT (content): Merge conflict in package-lock.json
Auto-merging package.json
CONFLICT (content): Merge conflict in package.json
error: could not apply f5d281b... Add axios
hint: Resolve all conflicts manually, mark them as resolved with
hint: "git add/rm <conflicted_files>", then run "git rebase --continue".
hint: You can instead skip this commit: run "git rebase --skip".
hint: To abort and get back to the state before "git rebase", run "git rebase --abort".
Could not apply f5d281b... Add axios

We have to deconflict package.json ourselves. lockrebase does not do this.

The end of package.json looks like this

   ... rest of file
   
   "license": "ISC",
   "dependencies": {
<<<<<<< HEAD
     "left-pad": "^1.3.0"
=======
     "axios": "^1.6.2"
>>>>>>> f5d281b (Add axios)
  }
}

We want to keep both so we edit it to look like this:

   ... rest of file

  "license": "ISC",
  "dependencies": {
    "left-pad": "^1.3.0",
    "axios": "^1.6.2"
  }
}

Save package.json. Then git add package.json to mark as resolved. This file is now correct but the lock file is not only munged with git conflict markers but we also don't want to hand-edit a lock file. It's made for machines.

This is where lockrebase comes in. It's when you have this as your git status.

interactive rebase in progress; onto d9db4f2
Last command done (1 command done):
   pick f5d281b Add axios
No commands remaining.
You are currently rebasing branch 'add_axios' on 'd9db4f2'.
  (fix conflicts and then run "git rebase --continue")
  (use "git rebase --skip" to skip this patch)
  (use "git rebase --abort" to check out the original branch)

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	modified:   package.json

Unmerged paths:
  (use "git restore --staged <file>..." to unstage)
  (use "git add <file>..." to mark resolution)
	both modified:   package-lock.json

When the last thing to resolve is the lock file, you want to pull the lock file from main and regenerate it from your branch. I used to have aliases for every package manager for this situation.

alias bic='git checkout origin/main -- Gemfile.lock; bundle install; git add Gemfile.lock; git rebase --continue'
alias yic='git checkout origin/main -- yarn.lock; yarn install; git add yarn.lock; git rebase --continue'
alias pic='git checkout origin/main -- poetry.lock; poetry install; git add poetry.lock; git rebase --continue'
# continue for every package manager ... aaaaaaa

Resolving the lock file with lockrebase

Instead, use lockrebase.

lockrebase npm

# many things will happen
# if you want to see what or run it yourself you can do `lockrebase --print-only npm`

# A rebase git commit message will appear for "Add axios"
# Just save and exit

You are caught up and resolved:

› git status
On branch add_axios
Your branch is ahead of 'main' by 1 commit.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment