Skip to content

Instantly share code, notes, and snippets.

@vladshablinsky
Created August 1, 2016 13:38
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 vladshablinsky/06e91cc4cff728a6206774624566d04f to your computer and use it in GitHub Desktop.
Save vladshablinsky/06e91cc4cff728a6206774624566d04f to your computer and use it in GitHub Desktop.
short_commits_problem

Problem

The question raised in this PR is using long git commit hashes and short git commit hashes. The format of the HEAD-versions we use is the following: HEAD-<version>_<revision>, where version is a commit hash. There are plenty of download strategies, but I refer only to GitDownloadStrategy here, because others have either other 'commit' structure or no short-hashes support.

To compare two HEAD version we need to compare commit hashes. And this is what this PR is about. Suppose we only store long hashes. So, to detect if installed HEAD-version is outdated we need to compare upstream hash of the project with hash used in installed HEAD-version prefix. If they are the same, then installed HEAD is up-to-date, otherwise we have outdated HEAD.

Let's now suppose we use short hashes. What we do to detect outdated versions is a) calling VCSDownloadStrategy#last_commit which goes to cached location and gets the hash and b) compare returned last_commit with HEAD-prefix. If it any time last_commit increases its length, then installed HEAD-prefix cannot be equal to it anymore, thus, it's outdated.

More precisely:

$ ls /usr/local/Cellar/ack
HEAD-a0a0a0a

==> Interactive Homebrew Shell
Example commands available with: brew irb --examples
2.0.0-p481 :001 > downloader = Formulary.factory("ack").head.downloader
  => ...
2.0.0-p481 :002 > downloader.shutup!
  => "true"
2.0.0-p481 :003 > downloader.fetch_last_commit
  => "a0a0a0a0"
2.0.0-p481 :004 > ^D

That means that a0a0a0a is no longer unique within the repo, but it was when we installed HEAD-a0a0a0a, therefore HEAD is outdated.

The problem arises when we introduce GitGitHubDownloadStrategy to be able to detect outdated versions without fetching a repository. GitGitHubDownloadStrategy#last_commit returns full-length hash. The problem is that we can't check installed hash and fetched hash for equality anymore. What we can is to perform start_with? check, but it still can't guarantee the version is up-to-date.

Example:

$ ls $(brew --cellar)/ack
HEAD-b090608c

Let's now suppose we want to check if HEAD-b09060c is outdated using GitHub API, which return full-length prefix and let's suppose it returns the following hash:

b090608c49643099a47247d79ac9ca80d05c2ee0

How do we treat it? Is the HEAD version outdated or not? Suppose it's not since prefixes are the same. After a while we check it again and GitHub API returns the following hash:

b090608c00000000000000000000000000000000

So, we still think our installed HEAD is up-to-date, however that's not true.

Workaround

  • Store long hashes in Tab. Cons: complicate.
  • Use short commits and use == for VCSDownloadStrategy and start_with? for GitHubDownloadStrategy. Cons: there is a possibility we get outdated version (see the example).
  • Store long hashes everywhere. Cons: long hashes in outdated outputs and long prefixes.

For now long hashes used everywhere in the code and even if we use short hashes for git we cannot escape from long hashes for other VCSs.

CC @xu-cheng

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment