Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Git LFS and husky prepush hook? #108

Closed
orlin opened this issue Mar 8, 2017 · 42 comments
Closed

Git LFS and husky prepush hook? #108

orlin opened this issue Mar 8, 2017 · 42 comments

Comments

@orlin
Copy link

orlin commented Mar 8, 2017

I just tried to husky a prepush script and nothing happened. Looking at .git/hooks/pre-push I see that I already have a hook there:

#!/bin/sh
command -v git-lfs >/dev/null 2>&1 || { echo >&2 "\nThis repository is configured for Git LFS but 'git-lfs' was not found on your path. If you no longer wish to use Git LFS, remove this hook by deleting .git/hooks/pre-push.\n"; exit 2; }
git lfs pre-push "$@"

Running node node_modules/husky/bin/install.js doesn't help:

husky
setting up hooks
skipping pre-push hook (existing user hook)
done

I guess that's good, as I don't want Git LFS to break and get some large files in the repo without noticing...

Is there a husky solution for having a prepush while also using git-lfs?

@typicode
Copy link
Owner

Hi @orlin,

Good question. Would it be possible to move the logic out of the pre-push hook and have husky run it on pre-push?

{
  "scripts": {
    "prepush": "node ./scripts/git-lfs-exists.js"
  }
}

@joncursi
Copy link

joncursi commented Jun 4, 2017

@typicode I'm having the same issue. Willing to try it out but I'm not sure how to convert the git-lfs code to JS

@maxim-grishaev
Copy link

@typicode No.

package.json
"prepush": "git lfs pre-push \"$@\"", or "prepush": "git lfs pre-push",

husky > npm run -s prepush (node v8.1.2)

This should be run through Git's pre-push hook.  Run `git lfs update` to install it.

I suppose LFS requires parameters: https://git-scm.com/book/gr/v2/Customizing-Git-Git-Hooks

The pre-push hook runs during git push, after the remote refs have been updated but before any objects have been transferred. It receives the name and location of the remote as parameters, and a list of to-be-updated refs through stdin. You can use it to validate a set of ref updates before a push occurs (a non-zero exit code will abort the push).

@maxim-grishaev
Copy link

maxim-grishaev commented Jul 26, 2017

What would probably solve the issue: ability to add hooks code instead of replacing it.
For example:

"postinstall": "git lfs update --force && cd ./node_modules/husky && npm run install -- --append"

@rcalabro
Copy link

rcalabro commented Dec 8, 2017

+1

1 similar comment
@creaux
Copy link

creaux commented Dec 27, 2017

+1

@L0stSoul
Copy link

i have the same problem and to solve it i migrate from husky to git-hooks. both library is pretty similiar but git-lfs hooks work pretty well with git-hooks :)

@dperyel
Copy link

dperyel commented Mar 19, 2018

Try to add $GIT_PARAMS into your prepush script git lfs pre-push $GIT_PARAMS.
Or make some prepush.sh script with command -v git-lfs >/dev/null && git lfs pre-push "$@" and call it from your package.json prepush: sh ./prepush.sh $GIT_PARAMS

@rcalabro
Copy link

rcalabro commented Apr 7, 2018

+1

@GregTheGreek
Copy link

Any real solutions to this outside of manually editing the git hook...?

@Buthrakaur
Copy link

@GregTheGreek I switched to git-hooks-plus because of this problem and it works just well.

@GregTheGreek
Copy link

GregTheGreek commented Sep 24, 2018 via email

@pimjansen
Copy link

Have the same issue, i'd rather not switch again to a new tool.
Any update on this?

@jmchambers
Copy link

jmchambers commented Nov 6, 2018

I've managed to get this working with the latest version of husky (1.1.3):

package.json

  "husky": {
    "hooks": {
      "pre-push": "hooks/pre-push-lfs $HUSKY_GIT_PARAMS <<< $HUSKY_GIT_STDIN"
    }
  }

The trick was figuring out how to set STDIN for the command, using <<< $HUSKY_GIT_STDIN.

The script hooks/pre-push-lfs is just a file I created with the content that git lfs update --manual gives you:

hooks/pre-push-lfs

#!/bin/sh
command -v git-lfs >/dev/null 2>&1 || { echo >&2 "\nThis repository is configured for Git LFS but 'git-lfs' was not found on your path. If you no longer wish to use Git LFS, remove this hook by deleting .git/hooks/pre-push.\n"; exit 2; }
git lfs pre-push "$@"

Be sure to make it executable:

chmod +x hooks/pre-push-lfs

This got pre-push working for me. I think the other hooks probably need the same setup too (though haven't tested this yet):

package.json

  "husky": {
    "hooks": {
      "post-checkout": "hooks/post-checkout-lfs $HUSKY_GIT_PARAMS <<< $HUSKY_GIT_STDIN",
      "post-commit": "hooks/post-commit-lfs $HUSKY_GIT_PARAMS <<< $HUSKY_GIT_STDIN",
      "post-merge": "hooks/post-merge-lfs $HUSKY_GIT_PARAMS <<< $HUSKY_GIT_STDIN",
      "pre-push": "hooks/pre-push-lfs $HUSKY_GIT_PARAMS <<< $HUSKY_GIT_STDIN"
    }
  }

Hope that helps.

@pimjansen
Copy link

I've managed to get this working with the latest version of husky (1.1.3):

package.json

  "husky": {
    "hooks": {
      "pre-push": "hooks/pre-push-lfs $HUSKY_GIT_PARAMS <<< $HUSKY_GIT_STDIN"
    }
  }

The trick was figuring out how to set STDIN for the command, using <<< $HUSKY_GIT_STDIN.

The script hooks/pre-push-lfs is just a file I created with the content that git lfs update --manual gives you:

hooks/pre-push-lfs

#!/bin/sh
command -v git-lfs >/dev/null 2>&1 || { echo >&2 "\nThis repository is configured for Git LFS but 'git-lfs' was not found on your path. If you no longer wish to use Git LFS, remove this hook by deleting .git/hooks/pre-push.\n"; exit 2; }
git lfs pre-push "$@"

Be sure to make it executable:

chmod +x hooks/pre-push-lfs

This got pre-push working for me. I think the other hooks probably need the same setup too (though haven't tested this yet):

package.json

  "husky": {
    "hooks": {
      "post-checkout": "hooks/post-checkout-lfs $HUSKY_GIT_PARAMS <<< $HUSKY_GIT_STDIN",
      "post-commit": "hooks/post-commit-lfs $HUSKY_GIT_PARAMS <<< $HUSKY_GIT_STDIN",
      "post-merge": "hooks/post-merge-lfs $HUSKY_GIT_PARAMS <<< $HUSKY_GIT_STDIN",
      "pre-push": "hooks/pre-push-lfs $HUSKY_GIT_PARAMS <<< $HUSKY_GIT_STDIN"
    }
  }

Hope that helps.

This is not really a solution for me? This is just hacking in and renaming shipped hooks and this way the default shipped LFS hooks will probably die over time. The change needs to happen in Husky i think to append the husky hook implementation with the already existing one (or atleast give it as an option).

Ofcourse this will make husky not maintaining the LFS hook but in my case i also do not want it.

@AdrienLemaire
Copy link

@jmchambers thank you for the trick. It worked well for me on arch linux, but a coworker using Ubuntu gets the following error:

Switched to branch 'master'
Your branch is up to date with 'origin/master'.
husky > post-checkout (node v10.8.0)
/bin/sh: 1: Syntax error: redirection unexpected
husky > post-checkout hook failed (cannot be bypassed with --no-verify due to Git specs)

replacing bin/sh with bin/bash or bin/dash in the script didn't help.
Forcing bin/bash in the package.json hook call didn't help either.
The error only disappears when removing the <<< $HUSKY_GIT_STDIN part, so there's something with ubuntu that forces the usage of sh and fails on triple chevron redirection.
Any idea what the reason could be and how to solve it ?

@AdrienLemaire
Copy link

After reading a bit about here-strings, I modified the hook calls as follow

"echo $HUSKY_GIT_STDIN | hooks/pre-push-lfs $HUSKY_GIT_PARAMS"

@jmchambers
Copy link

Sorry for the delayed reply @Fandekasp, glad you got it sorted. I'm on mac, I guess here-strings are a platform-dependent thing. I prefer your syntax.

@pimjansen, I think the philosophy behind Husky is that all hooks should be outside the .git folder, in your app code, so that other people working on the app get all the same hooks as you when they checkout the repo. If you had a mix of .git folder hooks (like those LFS installs) and app-code hooks, then only the latter would be shared with the app's source code, which could lead to confusion.

@AdrienLemaire
Copy link

Just realized that somehow the .git/hooks/pre-push script got overrided with the git lfs hook code... not sure when and why

@mattrabe
Copy link

mattrabe commented May 17, 2019

To summarize suggestions by @jmchambers and @Fandekasp, the following works for me and keeps the hooks under version control as desired:

  1. Run git lfs update --manual
  2. Copy each of the scripts the above command spits out into new files under version control. NOT into the .git/hooks/ dir, but instead into a new dir in your repo at hooks/. Name them hooks/pre-push-lfs, hooks/post-checkout-lfs, etc
  3. Make those hook files executable: chmod +x hooks/*-lfs
  4. Point husky to the files you just saved in package.json:
  "husky": {
    "hooks": {
      ...your other hooks...
      "pre-push": "echo $HUSKY_GIT_STDIN | hooks/pre-push-lfs $HUSKY_GIT_PARAMS",
      "post-checkout": "echo $HUSKY_GIT_STDIN | hooks/post-checkout-lfs $HUSKY_GIT_PARAMS",
      "post-commit": "echo $HUSKY_GIT_STDIN | hooks/post-commit-lfs $HUSKY_GIT_PARAMS",
      "post-merge": "echo $HUSKY_GIT_STDIN | hooks/post-merge-lfs $HUSKY_GIT_PARAMS"
    }
  }

Yes, this does mean that if those hooks for LFS ever change that your hook files would need to be updated to stay in sync, but if you look at those hooks files they are pretty inert - they just call the git lfs commands that must possess the actual business logic for the hooks. So I would estimate that is a minor issue - and would be easily solved after a future breaking change by repeating the here-entailed process of saving the hooks from git lfs update --manual to files, easily enforced by noticing that running git lfs update spits out an error after said future update, just like the first time you ran into this which brought you here...

@mbelsky
Copy link

mbelsky commented Jul 16, 2019

@mattrabe thanks for summary. However there is an issue with that solution. After clone .git/hooks will contain a few .sample files and four hooks generated by git lfs:

  • post-checkout
  • post-commit
  • post-merge
  • pre-push

And husky won't overwrite that hooks on npm install. So be careful.

@mattrabe
Copy link

@mbelsky I believe your statement to be correct regarding files in .git/hooks - however, if I am reading correctly it does not affect my summary above, because the files in my summary are in hooks/, not in .git/hooks/. That is after all the whole point: put them somewhere other than below the .git dir so that they can be version controlled

@mbelsky
Copy link

mbelsky commented Sep 13, 2019

@mattrabe I've created a simple repo with husky and lfs. To reproduce the issue that I described, please do following steps:

  1. Check that git lfs is installed on your machine git lfs --version
  2. git clone https://github.com/mbelsky/husky-with-lfs.git
  3. cd ./husky-with-lfs
  4. npm i
  5. git checkout -b repro-108
  6. husky doesn't call post-checkout hook

Expected result:

On post-checkout hook husky echo 👋 Hey everyone from typicode/husky/issues/108

Why?

This happens because after cloning git lfs create a few hooks in .git/hooks folder and npm i doesn't overwrite them with husky hooks. There is an PR that could solve that issue.

@aviaryan
Copy link

@mbelsky I think if we just add a step 3.5 in there where we manually go to the hooks directory and delete all LFS hooks, then step 4, we can make this work. Not the best solution but this can be manageable for a small team.

@mbelsky
Copy link

mbelsky commented Sep 27, 2019

@aviaryan absolutely right. However this step break lfs support for a repo. So in addition it requires 4.5 step where a user manually merge lfs hooks in husky hooks.

@aviaryan
Copy link

@mbelsky Sorry but I don't see how we need that step. If we go by @mattrabe's post, LFS hooks should be already placed by husky (at npm i) since package.json and $project_root/hooks directory already has those hooks.

PS - It could be that I don't get how husky works. I am just working on a project that already has husky set up and I need to use LFS now.

@mbelsky
Copy link

mbelsky commented Sep 27, 2019

@aviaryan oh I've missed that we placed lfs hooks under $project_root/hooks. If we did it then we don't need to do this 4.5 step. npm i writes husky hooks (which triggers user hooks described in a config file) under $project_root/.git/hooks and later husky hooks will trigger lfs hooks if need

@dixhuit
Copy link

dixhuit commented Oct 31, 2019

Just tried @mattrabe 's suggestion but I always get push failed:

 > running pre-push hook: echo $HUSKY_GIT_STDIN | hooks/pre-push-lfs $HUSKY_GIT_PARAMS
This should be run through Git's pre-push hook.  Run `git lfs update` to install it.
pre-push hook failed (add --no-verify to bypass)

I'm running yorkie (fork of husky with no issue queue of its own) rather than husky because Vue CLI 3 but I'm led to believe that they're similar enough.

Any pointers?

@mattrabe
Copy link

@danbohea Did you run git lfs update as stated in the error?

@dixhuit
Copy link

dixhuit commented Oct 31, 2019

@mattrabe No I didn't, I thought that this would attempt to write to the .git/hooks/pre-push file that yorkie has already written to. Shall I run it anyway?

@dixhuit
Copy link

dixhuit commented Oct 31, 2019

@mattrabe I ran it:

$ git lfs update
Hook already exists: pre-push

        #!/bin/sh
        #yorkie 2.0.0
        
        command_exists () {
        command -v "$1" >/dev/null 2>&1
        }
        
        has_hook_script () {
        [ -f package.json ] && cat package.json | grep -q "\"$1\"[[:space:]]*:"
        }
        
        # OS X and Linux only
        load_nvm () {
        # If nvm is not loaded, load it
        command_exists nvm || {
        export NVM_DIR="$1"
        [ -s "$1/nvm.sh" ] && . "$1/nvm.sh"
        }
        }
        
        # OS X and Linux only
        run_nvm () {
        # If nvm has been loaded correctly, use project .nvmrc
        command_exists nvm && [ -f .nvmrc ] && nvm use
        }
        
        cd "."
        
        # Check if pre-push is defined, skip if not
        has_hook_script pre-push || exit 0
        
        # Add common path where Node can be found
        # Brew standard installation path /usr/local/bin
        # Node standard installation path /usr/local
        export PATH="$PATH:/usr/local/bin:/usr/local"
        
        # Try to load nvm using path of standard installation
        load_nvm /home/dan/.nvm
        run_nvm
        
        # Export Git hook params
        export GIT_PARAMS="$*"
        
        # Run hook
        node "./node_modules/yorkie/src/runner.js" pre-push || {
        echo
        echo "pre-push hook failed (add --no-verify to by

To resolve this, either:
  1: run `git lfs update --manual` for instructions on how to merge hooks.
  2: run `git lfs update --force` to overwrite your hook.

@mattrabe
Copy link

@danbohea Hm, I wish I could help out - I have not experienced that before.

@mbelsky
Copy link

mbelsky commented Nov 1, 2019

Hey @danbohea
I've adapted @mattrabe 's suggestion to a manual. Please, try it: https://dev.to/mbelsky/pair-husky-with-git-lfs-in-your-javascript-project-2kh0

@dixhuit
Copy link

dixhuit commented Nov 1, 2019

Thanks @mbelsky I now have something working with husky (eventually I need to get this playing nice with yorkie). Your guide really helped me understand what each tool is doing and when, especially when installing/initialising.

@lolmaus
Copy link

lolmaus commented Nov 12, 2019

Here are commands that work on Windows (in Git Bash), as well as *nix:

"husky": {
  "hooks": {
    "pre-commit": "lint-staged",
    "post-checkout": "cross-env-shell echo $HUSKY_GIT_STDIN | cross-env-shell sh lfs-hooks/post-checkout $HUSKY_GIT_PARAMS",
    "post-commit": "cross-env-shell echo $HUSKY_GIT_STDIN | cross-env-shell sh lfs-hooks/post-commit $HUSKY_GIT_PARAMS",
    "post-merge": "cross-env-shell echo $HUSKY_GIT_STDIN | cross-env-shell sh lfs-hooks/post-merge $HUSKY_GIT_PARAMS",
    "pre-push": "cross-env-shell echo $HUSKY_GIT_STDIN | cross-env-shell sh lfs-hooks/pre-push $HUSKY_GIT_PARAMS"
  }
},

The cross-env npm package must be installed as a dev-dependency:

npm i -D cross-env
yarn add -D cross-env

@FullStackForger
Copy link

@lolmaus or @mattrabe solution. What if I want to run lint-staged in pre-push hook?

@FullStackForger
Copy link

@lolmaus or @mattrabe solution. What if I want to run lint-staged in pre-push hook?

Ignore above. Command chaining simply does the job. No need for anything fancier.

 "pre-push": "yarn build && bash echo $HUSKY_GIT_STDIN | bash lfs-hooks/pre-push $HUSKY_GIT_PARAMS"

Might not look pretty but for multiple commands but those can either be wrapped in npm script command or run as bash script that exits with non 0 for errors.

@ghost
Copy link

ghost commented May 11, 2020

Reading through this issue it seems like there are a lot of potential solutions, but tbh. I'd like feedback from the lib author as to what is a canonical solution to this problem from husky's perspective.

The original question in the top comment was:

Is there a husky solution for having a prepush while also using git-lfs?

@typicode, when you have time, it'd be nice to have your feedback on what you'd prefer as the solution. Ideally we'd then also add a test case to ensure that the canonical solution won't break. Until then I don't feel safe implementing this in a project.

@jdtzmn
Copy link

jdtzmn commented May 29, 2020

Not sure how to test it, but I think this code might work. I've opened a pr.

Can someone take a look: #729?

Update: I have tested it on one of my repositories and it worked

@typicode
Copy link
Owner

typicode commented Apr 4, 2021

Closing as it should be easier now to use Git LFS with husky's new approach.

@typicode typicode closed this as completed Apr 4, 2021
Gweaton added a commit to ministryofjustice/hmpps-interventions-ui that referenced this issue Jul 1, 2021
Git Large File Storage[0] is used for versioning large files. We believe
it's necessary for storing the Structured Interventions pdf in our repo.

It's tricky to set up in conjunction with our Husky linting git hook, so
I've followed the instructions on a [GitHub
issue](typicode/husky#108 (comment))

[0]: https://git-lfs.github.com/
Gweaton added a commit to ministryofjustice/hmpps-interventions-ui that referenced this issue Jul 1, 2021
Git Large File Storage[0] is used for versioning large files. We believe
it's necessary for storing the Structured Interventions pdf in our repo.

It's tricky to set up in conjunction with our Husky linting git hook, so
I've followed the instructions on a [GitHub
issue](typicode/husky#108 (comment))

[0]: https://git-lfs.github.com/
Gweaton added a commit to ministryofjustice/hmpps-interventions-ui that referenced this issue Jul 2, 2021
Git Large File Storage[0] is used for versioning large files. We believe
it's necessary for storing the Structured Interventions pdf in our repo.

It's tricky to set up in conjunction with our Husky linting git hook, so
I've followed the instructions on a [GitHub
issue](typicode/husky#108 (comment))

[0]: https://git-lfs.github.com/
Gweaton added a commit to ministryofjustice/hmpps-interventions-ui that referenced this issue Jul 2, 2021
Git Large File Storage[0] is used for versioning large files. We believe
it's necessary for storing the Structured Interventions pdf in our repo.

It's tricky to set up in conjunction with our Husky linting git hook, so
I've followed the instructions on a [GitHub
issue](typicode/husky#108 (comment))

[0]: https://git-lfs.github.com/
lawrence-forooghian pushed a commit to ministryofjustice/hmpps-interventions-ui that referenced this issue Jul 2, 2021
Git Large File Storage[0] is used for versioning large files. We believe
it's necessary for storing the Structured Interventions pdf in our repo.

It's tricky to set up in conjunction with our Husky linting git hook, so
I've followed the instructions on a [GitHub
issue](typicode/husky#108 (comment))

[0]: https://git-lfs.github.com/
@sag1v
Copy link

sag1v commented Apr 5, 2022

@typicode Can you share a link to a resource (or explain here) that explains why or how husky@V5 solves this issue?

@ericksprengel
Copy link

@sag1v

husky v4 installs husky on default git hooks folder .git/hooks and the custom hooks in package.json.
While husky v8 changes git hooks folder from .git/hooks/ to .husky/ and our custom hooks are in shell script files in .husky/ instead of in the package.json.

As we can see in the below example, after migrating to v8, I added the git lfs install to .husky/pre-push

⚠️ migrate to v8 before installing lfs (git lfs install) https://typicode.github.io/husky/#/?id=migrate-from-v4-to-v8

My migration from v4 to v8 (adding git-lfs too)

IT IS JUST AN EXAMPLE FOR pre-push, GIT LFS HAS MORE HOOKS (post-checkout, post-commit and post-merge ), USE git lfs install TO INSTALL THEM

v4

package.json:

  "husky": {
    "hooks": {
      "pre-push": "yarn lint"
    }
  }

v8 (with git-lfs):

package.json:

  "scripts": {
    "prepare": "husky install"
  },

.husky/pre-push:

#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

# my custom hook
yarn lint

# git-lfs hook
command -v git-lfs >/dev/null 2>&1 || { echo >&2 "\nThis repository is configured for Git LFS but 'git-lfs' was not found on your path. If you no longer wish to use Git LFS, remove this hook by deleting '.git/hooks/pre-push'.\n"; exit 2; }
git lfs pre-push "$@"

As we can see, .husky/pre-push is more flexible (shell script), is under git version control and uses the original .git/hooks files that are compatible with git lfs install.

I tried to be short, but it's not easy to explain with few words. 😅

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests