> ## Documentation Index
> Fetch the complete documentation index at: https://site.aspect.build/llms.txt
> Use this file to discover all available pages before exploring further.

# Easier merges on lockfiles

> Resolve lockfile merge conflicts automatically with Git's 'ours' merge driver, streamlining your workflow and avoiding manual fixes.

export const BlogPost = ({title, date, authors, tags, image, children}) => {
  const tagList = tags ? tags.split(", ").filter(Boolean) : [];
  const tagSlug = t => t.toLowerCase().replace(/&/g, "").replace(/\+/g, "").replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
  const formattedDate = date ? new Date(date + "T00:00:00").toLocaleDateString("en-US", {
    year: "numeric",
    month: "long",
    day: "numeric"
  }) : "";
  return <section className="w-full flex justify-center px-4 py-12 md:py-16">
      <div style={{
    maxWidth: "800px",
    width: "100%"
  }}>
        {image && (typeof image === "string" ? <img noZoom src={image} alt={title} className="w-full rounded-xl mb-8" style={{
    maxHeight: "400px",
    objectFit: "cover"
  }} /> : <div className="blog-post-hero-image">{image}</div>)}
        <h1 className="text-3xl md:text-4xl font-bold text-zinc-900 dark:text-white">
          {title}
        </h1>
        <div className="flex flex-wrap items-center gap-3 mt-4 text-sm text-zinc-500 dark:text-zinc-400">
          {authors && <span>{authors}</span>}
          {authors && formattedDate && <span>·</span>}
          {formattedDate && <span>{formattedDate}</span>}
        </div>
        {tagList.length > 0 && <div className="flex flex-wrap gap-2 mt-3">
            {tagList.map(tag => <a key={tag} href={"/blog/tags/" + tagSlug(tag)} className="px-2 py-0.5 rounded-full text-xs bg-zinc-100 dark:bg-zinc-800 text-zinc-600 dark:text-zinc-400 hover:bg-blue-100 dark:hover:bg-blue-900/40 hover:text-blue-700 dark:hover:text-blue-300 transition">
                {tag}
              </a>)}
          </div>}
        <hr className="my-8 border-zinc-200 dark:border-zinc-700" />
        <div className="prose dark:prose-invert max-w-none">{children}</div>
      </div>
    </section>;
};

export const MarketingPage = () => <div className="marketing-page-marker" style={{
  display: "none"
}} />;

export const Section = ({children, className = "", gray = false, dark = false, id}) => <section id={id} className={`w-full flex justify-center px-4 py-16 md:py-24 ${gray ? "bg-gray-50 dark:bg-zinc-900" : dark ? "bg-zinc-900 dark:bg-zinc-950" : ""} ${className}`}>
    <div className="w-full" style={{
  maxWidth: "1140px"
}}>
      {children}
    </div>
  </section>;

<MarketingPage />

<BlogPost title="Easier merges on lockfiles" date="2024-01-22" authors="Alex Eagle" tags="Monorepo, JavaScript, Bazel">
  <img noZoom src="https://mintcdn.com/aspectbuild/x1L7Iep716jCyJVo/images/blog/hashnode/hn-9859a7d82b.jpeg?fit=max&auto=format&n=x1L7Iep716jCyJVo&q=85&s=f17678cae0fa97182f07910fa550feab" alt="" className="blog-post-cover" width="1254" height="836" data-path="images/blog/hashnode/hn-9859a7d82b.jpeg" />

  Lockfiles are a strange case of code which is checked into your repository, but not really editable. When you change your third-party dependencies, you'll typically be forced to update a corresponding lockfile at the same time. What happens when you rebase your changes on upstream, and someone else also updated the lockfile? Merge conflicts! Yuck!

  Resolving a merge conflict in some generated file is annoying. Do you always accept your changes? Always accept the "incoming"? Some package managers know how to resolve merge conflicts on their own, if you remember this feature then your happy path is just to run the package manager again (e.g. `pnpm install`) and then the file is fixed. But not all engineers know that this works, and not all package managers know to do the resolution. (For example, Bazel's bzlmod, see [https://github.com/bazelbuild/bazel/issues/20272#issuecomment-1819889397](https://github.com/bazelbuild/bazel/issues/20272#issuecomment-1819889397))

  Git already has a way to improve the situation though. The `.gitattributes` file has a bunch of helpful bits you should know about, such as `linguist-generated=true` (mark some files as generated so GitHub doesn't show diffs in their content) and `export-ignore` (omit some files when packaging up an archive of the repo). Let's look at another one: `merge=`

  Git has a bunch of strategies for merging file contents, called "drivers". You can register some of these in your personal git configuration, see [https://git-scm.com/docs/merge-config](https://git-scm.com/docs/merge-config). The driver is a program that is expected to perform the merge. Again from the [docs](https://git-scm.com/docs/gitattributes#_defining_a_custom_merge_driver):

  > a command to run to merge ancestor’s version (`%O`), current version (`%A`) and the other branches' version (`%B`)\
  > ...\
  > The merge driver is expected to leave the result of the merge in the file named with `%A` by overwriting it, and exit with zero status if it managed to merge them cleanly, or non-zero if there were conflicts.

  Let's say we want to take the "current version" (whatever we have in our local source tree) and make that the merge result. It's already the `%A` file, so there's nothing to do. All we have to do is "exit with zero status" and that's what the `true` builtin does. So while the merge driver could be a complex topic, there's a trivial way to define one that always takes "our" file as the merge result:

  ```plaintext theme={null}
  git config --global merge.ours.driver true
  ```

  Now that you've got that in your git configuration (and every other developer on your team does as well...) you can specify that the lockfiles in your repo are meant to always use this merge driver, by adding to your `.gitattributes` file like

  ```plaintext theme={null}
  path/to/LOCKFILE merge=ours
  ```

  Now you shouldn't be bothered with merge conflicts like the default driver would report.
</BlogPost>
