184

I've got a project that I'm deploying to Heroku. The source code tree includes a bunch of mp3 files (the website will be for a recording project I was heavily involved with).

I'd like to put the source code for it up on GitHub, but GitHub has a 300 MB limit on their free accounts. I don't want to use 50 MB of my limit on a bunch of mp3 files. Obviously, I could add them to the .gitignore file to keep them out of my repo.

However, I deploy to Heroku using git push heroku. The mp3 files must be present in the branch I push to Heroku so that they get get deployed.

Ideally, I'd like to .gitignore the mp3 files in my local master branch so that when I push that to GitHub, the mp3s are not included. Then I'd keep a local production branch that has the mp3s committed rather than ignored. To deploy, I would merge master into production, and then push the production branch to Heroku.

I can't get this to work right.

Here's an example of what I'm trying to do...

$ git init git-ignore-test
$ cd git-ignore-test
$ echo "*.ignored" >> .gitignore
$ git add .gitignore && git commit -m "Ignore .ignored files"
$ touch Foo.ignored

At this point, Foo.ignored is ignored in my master branch, but it's still present, so my project can use it.

$ git checkout -b unignored
$ cat /dev/null > .gitignore
$ git add Foo.ignored .gitignore && git commit -m "Unignore .ignored files"

Now I've got a branch with these files committed, as I want. However, when I switch back to my master branch, Foo.ignored is gone.

Anyone got any suggestions for a better way to set this up?

Edit: just to clarify, I want the mp3 files to be present in both branches so that when I run the site locally (using either branch) the site works. I just want the files ignored in one branch so when I push to GitHub they are not pushed as well. Usually .gitignore works well for this kind of thing (i.e. keeping a local copy of a file that does not get included in a push to a remote), but when I switch to the branch with the files checked in, and then back to the branch with the files ignored, the files vanish.

Mateusz Piotrowski
  • 8,029
  • 10
  • 53
  • 79
Myron Marston
  • 21,452
  • 5
  • 64
  • 63
  • Why do the MP3 files *ever* need to be committed to the repository? – Dan Loewenherz Dec 03 '09 at 02:34
  • 4
    With heroku, committing to your repo is the only way to get files included with your app when you deploy. The only alternative is to use something like Amazon S3 to serve the mp3s, but I'd prefer to avoid that. – Myron Marston Dec 03 '09 at 02:40
  • It sounds like you're tripping over dollars to save pennies... The Rackspace Cloud is very simple to get setup on, and it would cost very little to store < 1 GB of files there... – gahooa Dec 09 '09 at 20:31
  • 3
    I notice you marked an answer as correct but from what I've read it may not be correct. Did you ever actually try the answer and did it work for you? – loop Jan 31 '12 at 22:08
  • @MyronMarston How did you get overcome this challenge? – user805981 Nov 12 '15 at 17:02
  • Has this issue been solved? I'm struggling with almost the same thing. Consider I have a `master` branch for production and `development` branch for development. `master` branch ignores source files (assembled by `gulp`, for instance), whereas `development` branch ignores these assembled and minified files. When I switch back and forth from `master` to `development`, my files keep disappearing, breaking everything :( – Anton Egorov Apr 16 '16 at 08:56
  • This is also a practical question for anyone who has FORKED A REPO and wants to keep the fork to contrib to it, but also use it as an app, on a separate branch. Each time they find improvements using the framework branch, they can switch branches back to fork-origin then do a push of their fix. Thats what im running into. I want an ignore on certain files, like the home page mainly, so i can make sure and add the H2 tag on the index template to identify itself, so i dont get it mixed up. A quick edit on the home page, and i need an ignore on that branch only. – blamb Nov 08 '16 at 00:32
  • I wrote a npm module to achieve this. Would love to hear if this works for your needs. https://www.npmjs.com/package/build-ignore – Davidicus Mar 20 '17 at 15:37
  • The edit queue is full right now, but there are https versions of the URLs in the above post that should be edited in. (https://github.com/ and https://www.heroku.com/) – AJM Oct 28 '22 at 12:51

9 Answers9

85

This solution appears to work only for certain, patched versions of git. See a new answer pointing to workarounds and another answer and subsequent comments for a hint which versions may work.

I wrote a blog post on how to effectively use the excludesfile for different branches, like one for public github and one for heroku deployment.

Here's the quick and dirty:

$ git branch public_viewing
$ cd .git/
$ touch info/exclude_from_public_viewing
$ echo "path/to/secret/file" > info/exclude_from_public_viewing 

then in the .git/config file add these lines:

[core]
excludesfile = +info/exclude

  
[branch "public_viewing"]
excludesfile = +info/exclude_from_public_viewing

Now all the global ignore stuff is in the info/exclude file and the branch specific is in the info/exclude_from_public_viewing

http://cogniton-mind.tumblr.com/post/1423976659/howto-gitignore-for-different-branches

starball
  • 20,030
  • 7
  • 43
  • 238
Cognition.Mind
  • 1,274
  • 9
  • 7
  • Fantastic. I never new about `excludesfile`. Thanks! – Myron Marston Oct 29 '10 at 16:01
  • 1
    Sorry for taking so long.. but if you don't delete the .gitignore files it will default to those first. – Cognition.Mind Aug 07 '11 at 07:28
  • 4
    This does not work for me, even when the project does not use .gitignore. Do you mind providing a transcript of commands setting this up from scratch (`git init`, ...), and the version of Git you used? – Davor Cubranic Jan 18 '12 at 21:42
  • Can I easily apply this solution to ignore a version of a file in master. So, any changes merged by accident will not overwrite contents of a particular file in master? Would be awesome if I could flag the merge if I ever wanted to overwrite it. Git it so awesome, I start looking for ridiculous solutions, and most times I find it. – madphp Jul 15 '13 at 13:33
  • 40
    This solution does not work. There was some discussion on the blog, and no one there was able to make this work either. – spuder Oct 02 '13 at 05:23
  • Older thread, but this absolutely works -- I just did it to ignore a set of files on a separate branch. – ABach Feb 21 '14 at 03:55
  • @spuder This is because you need to write "branch" instead of "local" in .git/config. I've corrected the post. – Vladislav Rastrusny Nov 05 '14 at 16:23
  • That page is doing some crazy weird stuff when I try to view it. Looks like some bad Javascript code. I've added it to archive.org, and that makes it readable: http://web.archive.org/web/20150213155046/http://cogniton-mind.tumblr.com/post/1423976659/howto-gitignore-for-different-branches – Kirkland Feb 13 '15 at 15:51
  • 10
    Related: [my answer to a SO question about this solution not working](http://stackoverflow.com/a/29583813/2157640) – Palec May 18 '15 at 18:38
  • Tried that with git 2.7.4 and it didn't work. `git stash -u -a` seems the way to go for me, but it's tedious when the directories to ignore contain some gigabytes. – Murphy Sep 01 '16 at 15:04
  • 3
    In vanilla Git, I am pretty sure this does not work and never did. Those of you who managed to reproduce this, can you share your Git version and other relevant info on your environment? Maybe you have a patched version from your distribution's Git package maintainers. – Palec Sep 02 '16 at 08:35
  • See answer from Murphy, Sep 1st, 2016. Seems like this approach is not working anymore. – BerndGit Feb 10 '22 at 13:27
  • This does not work for me either, but I am unable to remove my upvote. Apparently it's "locked in". – AJM Oct 28 '22 at 13:44
29

Important hint: The accepted answer by Cognition.Mind doesn't work (anymore, for several years now, or perhaps for vanilla versions of git); see the comments. A valid answer and workaround can be found here:

https://stackoverflow.com/a/29583813/2157640

Another alternative workaround (working for my particular problem, but demanding manual stash operations or implementation of a hook) would be git stash -u -a. This is tedious when the differences are large.

And finally, the solution I'm now going with is having forked my VM that we hold our development environment in, and set up info/excludes appropriately for the branch, respectively deleting the offending, uncommitted files/folders.

Community
  • 1
  • 1
Murphy
  • 3,827
  • 4
  • 21
  • 35
16

Let's say we want to ignore build folder from all other branch except production branch . As we want to push build folder in production.

1) Dont include build in .gitignore . If you do that it will always be ignored for all branches .

2) Create a file exclude_from_public_viewing inside ./.git/info (This folder already exists) folder touch ./.git/info/exclude_from_public_viewing

3) Inside exclude_from_public_viewing write one line (As you are trying to ignore build for all the branches). !build

4)There's an existing file .git/info/exclude . We need to add following line in it.

 build

We want to ignore build folder but hasn't added it in .gitignore . So how git will know what to ignore ? Answer is we are adding it to exclude file and conditionally passing that file to git config

5) Now we have to conditionally unignore build folder for production branch. to do that perform following

6) There is a existing file called ./.git/config we need to add following -

a) excludesfile = +info/exclude below [core]

[core]
     excludesfile = +info/exclude

b) Create a new section in the end of ./.git/config as

[branch "production"]
    excludesfile = +info/exclude_from_public_viewing

Solution 2

There is one smart alternate solution . Lets say you want to add build/ folder in production branch and ignore it in all other branches.

1) Add it to your gitignore file.

2) In production branch, while doing git add, force add build folder: git add -f --all build/

trolzen
  • 119
  • 7
sapy
  • 8,952
  • 7
  • 49
  • 60
  • 2
    And this differs from the accepted solution how? With which version of git did you test it? As the linked answer states it fails because there's no support for `branch..excludesfile` in git (anymore?), only for `core.excludesfile`, and my own tests seemed to affirm this. – Murphy Sep 02 '16 at 08:15
  • @murphy it works fine with `git version 2.7.4 (Apple Git-66)` and I haven't used `branch..excludesfile ` in my solution. – sapy Sep 02 '16 at 08:43
  • 4
    @sapy: I tried this according to OP's use-case, and the file still vanishes when I switch from production to master. Note that the "build" folder should be committed into the "production" branch. (Also, I note that if I don't commit the "build" folder in "production" branch, the "production" branch ignores the presence of "build" folder -- i.e., the [branch "production"] excludesfile config doesn't work). I use exactly the same version of git as yours. I also tried the [branch "production"] approach to *ignore* file only in production branch, it also doesn't work, like Murphy said. – justhalf Sep 06 '16 at 02:58
  • Solution 2 was something that I was looking for. Thanks! – Vyacheslav Cotruta Dec 07 '16 at 10:52
  • @sapy How can you claim you haven't used `branch..excludesfile`? You've added an `excludesfile` under `[branch "production"]`! – AJM Oct 28 '22 at 13:47
  • Solution 2 doesn't work: The `build/` folder still vanishes when checking out of `production` branch to other branches. – אלימלך שרייבר Jan 12 '23 at 10:33
14

I would strongly advise considering putting those MP3 files on S3. Having them be part of your Heroku push (and thus part of your Heroku slug) will greatly slow down your dyno startup time. Since Heroku uses EC2, if the files are on S3 and are only accessed by your app (if users aren't directly linked to S3) you won't even pay any bandwidth charges, only the charge to store 50MB.

David Dollar
  • 2,409
  • 17
  • 18
5

Have you tried having .gitignore be different in your branch?

You should be able to ignore what you want based on the branch you are in as long as the files are not tracked on that branch.

kEND
  • 7,379
  • 3
  • 19
  • 13
  • 6
    I've tried that. If you look at the commands I posted above you'll see that's exactly what I did. The problem is that when I switch from the branch with the files checked in to the branch with the files ignored, git discards the files. I want to keep the mp3s in my master branch so the site works properly in dev mode, even though the files are ignored. – Myron Marston Dec 03 '09 at 19:00
  • 1
    he already said thats what he did. maybe just deleting this answer might be best :-) its causing distraction. – blamb Nov 08 '16 at 00:35
4

1. Summary

  1. I use Travis CI for deploying (it supports Heroku deployment)
  2. I add to my .travis.yml:

    before_deploy:
    - mv misc/.gitignore .gitignore
    

    Where misc — any folder, contains another .gitignore.

    mv UNIX command move file; overwrite, if file already exist.

When Travis CI deploy project, Travis CI will not push to deploying provider files and folders, that ignore in misc/.gitignore (not in the original .gitignore of sources).


2. Limitations

  1. This answer may not be suitable for all conditions of the author. But this answer answers the question “Using git, how do I ignore a file in one branch but have it committed in another branch?”
  2. I'm not Heroku user, my examples for GitHub, not for Heroku. Data of this answer works for me in GitHub, but may doesn't work on Heroku.

3. Relevance

This answer is relevant for April 2018. In the future, the data of this answer may be obsolete.


4. Demonstration

my real project.

Example of successful deployment.

4.1. Task

I deploy my project from src branch to dest branch of the same repository.

I want, that file PaletteMira.suricate-profile:

  • Local machine — exists for all branches,
  • src remote branch — not exists,
  • dest remote branch — exists.

If I correctly understood the author of the question, he have similar task.

4.2. src

sources branch — SashaYAML.

Part of .travis.yml:

before_deploy:
- mv misc/.gitignore .gitignore

deploy:
  provider: pages
  on:
    branch: SashaYAML
  keep-history: true
  skip-cleanup: true
  target-branch: SashaDevelop
  repo: Kristinita/PaletteMira
  github-token: $GITHUB_TOKEN
  committer-from-gh: true
  project-name: PaletteMira
verbose: true

Part of .gitignore:

*.sublime-snippet
*.suricate-profile

Part of misc/.gitignore

*.sublime-snippet

*.suricate-profile not in misc/.gitignore.

PaletteMira.suricate-profile not exists in this branch remotely, but exists locally.

4.3. dest

destination branch — SashaDevelop

Part of .gitignore:

*.sublime-snippet

*.suricate-profile not in misc/.gitignore.

PaletteMira.suricate-profile exists for this branch remotely and locally.

4.4. Steps to reproduce

I enable PaletteMira GitHub repository for Travis CI → I set environment variable $GITHUB_TOKEN with value — my GitHub token → I make any commit to my src branch.

If no errors, I must get expected behavior.

Саша Черных
  • 2,561
  • 4
  • 25
  • 71
0

Can you commit and push from Heroku?

e.g. Add the audio, push them to github and into heroku, remove the files on the working copy on Heroku. Remove the audio from the repo but not from the disk, then push that change back to github.

Tom
  • 33,626
  • 31
  • 85
  • 109
0

Github now has support for Large file storage, see more here https://git-lfs.github.com/

John Lozano
  • 115
  • 1
  • 6
0

The original question is a little oder but I came across it since I've just written a medium article about it, I thought this might help someone.

I personally have solved this with Git hooks via husky - seems to be the easiest and most reliable solution.

Obviously, I suggest to read the article but here are the steps to reproduce

  • step 1: yarn add -D husky && yarn husky install
  • step 2: yarn husky add .husky/post-checkout
  • step 3: mkdir .husky/gitignores && cp .gitignore .husky/gitignores
  • step 4: touch .husky/gitignores/.gitignore_husky
  • step 5: copy and paste this gist content into .husky/post-checkout
Flo Ragossnig
  • 1,341
  • 14
  • 38