80

I am creating an open-source library in JavaScript and using Git to control versions and history.

I have several commits in which the LICENSE file is X (for example, MIT), but I would like to change that to Y (for example, Apache-2.0).

All commits are currently local, I haven't pushed anything to GitHub yet.

If I just add a new commit changing the LICENSE file, and then push everything to GitHub, my release will have the license Y but if someone browses git history, they will find commits in which the license was still X.

Will someone be able to use my library with license X in this case? I don't want this to happen.

I could, of course, start a new repository with the correct license, but then I'd lose all history, and I don't want that either.

3
  • 13
    Licensing boils down to what you can claim in court, and win. As you state, you change the license before release, and leave the old file in history. If someone tried to fight that, and claim it was previously released with the old license, you'd need a way to prove otherwise. So yeah, better to avoid the issue entirely.
    – phemmer
    Commented Jun 10, 2018 at 5:02
  • 5
    @Patrick: I wouldn't put it that simple. This is not criminal law, the case will hinge on whose argument is the most convincing. The basic position in copyright law is that copying is not allowed, so the other party has to show that they have a license.That includes showing when they got the license. Here's the problem: if they claim an old date, it will predate the release of the software. If they claim a recent date, then only the new license would have been on offer.
    – MSalters
    Commented Jun 11, 2018 at 15:39
  • I'm sure that if the license said DRAFT at the top, then the courts would decide it had no legal standing; and it's quite possible that the court could be persuaded that a license file that was already superseded by another version at the time of first publication has the same standing as one that is marked DRAFT. But courts aren't predictable so it would be unwise to risk it. Commented Jun 12, 2018 at 7:34

3 Answers 3

71

This is a tricky question. Open source licenses are generally irrevocable. Once you publish something under a license, you can relicense future versions but cannot retroactively change the license. Yet here, you have never directly published the project under the old license.

My take on this is that the MIT license on the old commits is in effect for those commits, and other people are free to use old versions under that license.

But since you haven't yet published this project, you are free to rewrite the Git history. This is fairly advanced Git usage, but you don't have to lose all history. Instead, write a parallel history that keeps the commit metadata and just changes the license file. The git filter-branch command can be used to automate this process.

For example, we can use the following command to erase a LICENSE file from the history. The result will be that these old versions are “all rights reserved”; you can later add the intended license when you publish the project.

git filter-branch --index-filter 'git rm --cached --ignore-unmatch LICENSE' HEAD

Another great tool to edit, remove, and reorder commits is git rebase --interactive. However, filter-branch allows you to automate edits, and could e.g. also be used to remove license headers from all source files.

Careful: if something goes wrong during the history rewrite, it can be difficult to undo those changes. Before you begin, it is best to create some kind of backup, e.g.:

  • create a backup of your project directory incl. the .git/ directory.
  • create a new branch on which you perform the history rewrite. If everything goes well, you can update the old branch to point to the rewritten history (e.g. use git reset --hard to move the current branch to a specific commit).
  • recently used commits are also available through the git reflog.

Example of applying filter-branch:

Given this example history:

$ git log --name-status 
commit cb42eda9f7ab47ec55a2698fb7616a74bca4b043 (HEAD -> master)
Author: amon <[email protected]>
Date:   Sat Jun 9 20:12:12 2018 +0200

    some editing

A       bar.c
M       foo.c

commit a25c2949ca4c1d397c19510b0b05ebbb0266268b
Author: amon <[email protected]>
Date:   Sat Jun 9 20:11:30 2018 +0200

    adding LICENSE

A       LICENSE

commit 66465e97303c209a239eb212f2dc1b6b76de2cf8
Author: amon <[email protected]>
Date:   Sat Jun 9 20:09:55 2018 +0200

    initial commit

A       foo.c

When I run the license filter, then I get this history:

$ git log --name-status 
commit 25ea86eecda1415bf985f1cc03afbc0a30ece281 (HEAD -> master)
Author: amon <[email protected]>
Date:   Sat Jun 9 20:12:12 2018 +0200

    some editing

A       bar.c
M       foo.c

commit 505560e9a619b83c71531795cad9ff7a31a93e67
Author: amon <[email protected]>
Date:   Sat Jun 9 20:11:30 2018 +0200

    adding LICENSE

commit 66465e97303c209a239eb212f2dc1b6b76de2cf8
Author: amon <[email protected]>
Date:   Sat Jun 9 20:09:55 2018 +0200

    initial commit

A       foo.c

As you can see, all commits are still there and have the correct metadata. Those commits that were rewritten now have different IDs. We now have an empty commit where we originally added the license. It is possible to get rid of that as well (e.g. I'd use a git rebase --interactive to squash it), but it's not terribly important.

11
  • 2
    just a note on backing up the repository: you don't need to write down the HEAD commit id; just create a branch named backup or something. All a branch is is a name associated with a commit ID, and as long as a commit is reachable from a branch, it will never get garbage-collected.
    – user371366
    Commented Jun 10, 2018 at 15:51
  • 2
    @Hamsterrific i believe git push only pushes the objects associated with your branch. i could be mistaken though. but if you want to be sure, delete all branches (including any feature branches) that include the old commits, and then git gc should remove all traces.
    – user371366
    Commented Jun 10, 2018 at 15:57
  • 7
    git filter-branch is not destructive. A new tree object will be created for every changed directory, and its parent directory etc., and then a new commit pointing to its parent commit and that new directory, starting from the oldest change done by the rewrite. Git does prevent costly mistakes. The old branch tip will still exist. git reflog will show it. If reflog is disabled (not a default), git fsck will find it. @dn3s is correct, create a temporary head (tag or branch), and, if happy with the rewrite, delete it afterwards. No need to write down the SHA. Commented Jun 10, 2018 at 23:32
  • 4
    @Hamsterrific: Also, to drop empty linear commit(s) resulting from the deletion of all commit's changes, like the 505560e in amon's example, you can add a --prune-empty option to filter-branch. Commented Jun 11, 2018 at 0:16
  • 8
    @MichaelKay a different UK lawyer, speaking on the subject at FOSDEM this year, gave a fuller but more complete treatment: "A bare license ... is a promise... If this promise is not enshrined in a contract ... the person making the promise may withdraw it at any time. So English courts a long time ago came up with the principle of estoppel, which says that if someone makes a promise, and you rely on that promise, they may not later revoke that promise."
    – MadHatter
    Commented Jun 11, 2018 at 7:20
13

Licenses are not part of the software but govern a particular distribution of software. The inclusion of license files in distributive media does not, in itself, constitute a license. The same source code can be distributed with different licenses: as the author you can grant the inclusion in proprietary code, for example, without this necessitating a version with changed licensed headers and files to be sent out.

So from a legal point of view, you are theoretically fine. However, going after licensing violations in a manner minimizing legal costs and business reputation problems very much makes it desirable to hit people in the face with the license. For a "public license", redistribution under the same terms is usually permitted so attaching a license file to the actual media in distribution makes a lot of sense.

If you haven't previously published or distributed your software in the form of a publicly accessible repository but attached a license file nevertheless, I would strongly recommend that you make yourself acquainted with git filter-branch in order to replace the license files/notices in previous commits.

You can either replace the old license notices with the license you now want to distribute under, or you can put in a license that states that this is only for historic reference. I'd strongly suggest against the latter since it opens all kind of new headaches.

Keep in mind that putting up a Git repository means publishing all contained code in all iterations so you want to put appropriate licensing notices in it referring to your current licensing. When there have been previous publications, I'd recommend against rewriting history since it becomes rather tricky then to prove what license any version had been originally received under. But if you didn't previously publish, the history of the repository is yours to tamper with, and using git filter-branch is the tool to do that.

This is not falsification since a repository is a tool to work with, not a historic record. Part of its utility for distributed development depends on its history not changing after publication, but you haven't published yet.

2
  • The statement "Licenses are not part of the software but govern a particular distribution of software." is conjectural: it's up to the courts to decide. The MIT license starts "Permission is hereby granted, free of charge, to any person obtaining a copy of this software", and it's very unspecific about exactly what "this software" is, for example whether it covers everything in the repository regardless when it was added. Commented Jun 12, 2018 at 7:28
  • @MichaelKay stipulating that anything in a license, or indeed any kind of contract, is conjectural until decided on by a court, user11326's point seems a pretty good one. Even the MIT license you quote acknowledges that a copy must be transmitted for a licence to somehow inhere.
    – MadHatter
    Commented Jun 13, 2018 at 6:14
11

if the LICENSE file is added in a single commit, then git rebase -i is an easy way to remove it.

git rebase -i <root commit> will give you a list of all commits after the first one in an editor. find the commit that adds the LICENSE file and delete it from the list.

pick 86a75a8 some commit
pick 2dc535d some commit
pick 264b12f add LICENSE <-- delete this line
pick 86b0365 some commit
pick 665937c some commit
pick d4f3a2e some commit
pick d84554c some commit
pick ec93b2c some commit

once the old LICENSE file is gone from the history, add the new LICENSE, and then you may use git rebase -i again to move that commit to the start of the history if you desire.

pick 86a75a8 some commit
pick 2dc535d some commit
pick 86b0365 some commit
pick 665937c some commit
pick d4f3a2e some commit
pick d84554c some commit
pick ec93b2c add new LICENSE <-- move this line to the top

if you are unfamiliar with git rebase -i this is the easiest way to use it, as you are only rearranging commits.

a more advanced way would be change the LICENSE, commit it, and then use git rebase -i to move the LICENSE change up in the history, and then fixup the old LICENSE commit:

pick 86a75a8 some commit
pick 2dc535d some commit
pick 264b12f add LICENSE
pick 86b0365 some commit
pick 665937c some commit
pick d4f3a2e some commit
pick d84554c some commit
pick ec93b2c change LICENSE <-- move this line

find the LICENSE change, move it just after the old LICENSE and change pick to fixup:

pick 86a75a8 some commit
pick 2dc535d some commit
pick 264b12f add LICENSE
fixup ec93b2c change LICENSE <-- this will effectively replace the old LICENSE
pick 86b0365 some commit
pick 665937c some commit
pick d4f3a2e some commit
pick d84554c some commit
pick ec93b2c some commit

finally you could also directly edit the commit with the LICENSE change. that however is only recommended if you are already familiar with git rebase -i

all of these actions are straightforward if the history does not contain any merge commits and any LICENSE change is in a separate commit that does nothing else. though the fixup method only requires the later LICENSE change to be in a separate commit. it will work fine even if the initial LICENSE commit contains other changes too.

either way, do this on a copy of the repo, or at least on a copy of the branch unless you are already very comfortable using git.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.