1

I'm no GIT expert and forgive me if this is the wrong forum for this, but I'm using GIT to deploy code to my production server which works fine, I simply do git push production master where production points to my WPEngine server and it will obviously be a copy of the master branch.

The problem with this approach is that we need to include everything that's needed for prod to work in the repo. This includes stuff like dist/ (generated CSS/JS etc) as well as vendor/ (composer installed third party libraries) which is obviously not ideal.

To combat this I had a (in my mind) simple idea of separate .gitignores. One for origin and one for production. The way I set this up is that I have my normal .gitignore and then I also have a .prodignore.

I then wrote a simple shell script (deploy.sh) that does the following:

  1. Go through every .prodignore file and rename it .gitignore:

    for prodignore in $(find . -name '.prodignore'); do
        gitignore=$(echo $prodignore | sed 's/prodignore/gitignore/')
        mv $prodignore $gitignore
    done
    
  2. Remove all files from GIT but keep them on disk: git rm -r --cached --quiet . (this takes care of removing files that are now in the new .gitignore - like src/)

  3. Now re-add all files with the new .gitignore: git add --all (this takes care of adding files that were previously in .gitignore - like dist/)

  4. Commit and push to the production remote: git commit --quiet -m 'Sleek Deploy' && git push --quiet --force production master

  5. Finally go back one commit so we undo all we just did: git reset HEAD~ && git reset --hard HEAD

This all works mostly fine too. The correct new files are pushed to production and my local repo as well as origin still look good and have no weird histories as far as I can tell.

What does not work however - which I only recently noticed - is that if I delete a file from my local machine, push the delete to origin and then deploy to production using the above steps the deleted file will not be removed.

Why is this? Is there something I can do to make sure it gets deleted? Is my entire approach to this flawed? Imo having separate .gitignore files for separate remotes is such a simple solution to this problem, but perhaps I've bitten off more than I can chew.

It's worth noting that If I don't --force push in step 4 the remote tells me we are out of sync (which makes sense seeing as it has a commit that doesn't actually exist anywhere else - right?)

Edit: A little more info:

  • When I did git push production master like normal, and production was a copy of master, I never had this issue

  • This does not have to do with what's in .gitignore. The files I'm talking about have never been in either .gitignore or .prodignore. I only mentioned them for background information and why I need to push a commit to production that is never pushed to origin. This just happens if I:

    1. Create any random file, say "test.txt"
    2. Push the file to origin
    3. Push the file to production using the above steps
    4. Delete the file
    5. Push the deletion to origin (the file is no longer in the repo)
    6. Push the deletion to production using the above steps (the file is still on the production server for some reason)

If I never push the file to production to begin with, just to origin and then remove it, it will never end up on production.

production is as far as I am aware a bare repo if that helps.

powerbuoy
  • 12,460
  • 7
  • 48
  • 78
  • This is a duplicate of every other question that asks why gitignore doesn't ignore already tracked files. It doesn't. gitignore is for preventing `add` to add files, if you have already committed the file, it will be tracked forever (or until you actually delete it and commit the new snapshot), regardless of how you try to prevent it with gitignore. The correct approach in your case would be to set up a build system (jenkins, teamcity, whatever) that reacts to pushes, grabs the code, compiles it, gathers all the dependencies, then deploys to the production server. – Lasse V. Karlsen Jan 28 '20 at 11:09
  • 1
    You say production is a bare repo, so then you must have what I just described then, some form of deployment procedure. A bare repository doesn't have a working folder, and thus have no local copy of the files other than embedded in the commits. Are you sure you have a bare repository? Everything else you describe tells me you don't. – Lasse V. Karlsen Jan 28 '20 at 11:10
  • If you actually do have a bare repository, and some build/deployment procedure to grab a copy of the relevant files from the repository and put in place, you can change that script to do whatever. But again, I doubt you have a bare repository. – Lasse V. Karlsen Jan 28 '20 at 11:11
  • "duplicate of every other question that asks why gitignore doesn't ignore already tracked files" If you actually read the question you'd see how that's not the case. I have no trouble at all updating the gitignore and then pushing the newly added and removed files to my remote using steps 2 and 3. "The correct approach in your case" you're right but we don't have that option with WPEngine. If I SSH into the server and look at the files they don't have a .git folder or anything like that, which made me think it's a bare repo. Unfortunately I have no access to anything else on the server. – powerbuoy Jan 28 '20 at 11:18
  • Note that `git push` *does not push files*. `git push` pushes *commits*. Commits *contain* files, but every commit is a full and complete snapshot of all of its files. If the receiving Git runs some command that tries to pick and choose copying specific files from specific commits, that's a deployment process. That's where your issue lies: in the deployment process. What the actual problem is, I don't know, but you will need to carefully inspect the deployment process. – torek Jan 28 '20 at 15:24
  • Note, too, that after pushing a commit that does not have a file, the *repository as a whole* can be said to still have the file. The *repository* is a collection of commits, and the old commits have the file. The *new* commit you just pushed, which is the one you want deployed, lacks the file. Some process at the other end must therefore *remove* the file from some deployment location: the *repository itself* is not a deployment, it's a collection of commits. So we're back to examining the deployment process. – torek Jan 28 '20 at 15:28
  • @torek thanks this makes sense to me now. I've reached out to my host and asked them if they can help. It seems this is likely not an issue with GIT at all but rather the way they actually copy files to the production server once I push them to their remote. – powerbuoy Jan 28 '20 at 19:06
  • @torek - do you think a better solution would be to actually push the production version to origin too, and also tag it, then push to production, then revert to the previous commit and push that to origin so as to go back to the development version? – powerbuoy Jan 29 '20 at 08:03
  • It depends on how *they* do deployment. If they use Git as a deployment tool, and if we know precisely *how* they use Git as a deployment tool, then we can make predictions about how this would work. In my opinion, Git is a terrible deployment tool. There are others; but I don't know of any really good ones. I think this is a space where someone could offer a real, practical product. – torek Jan 29 '20 at 08:06
  • I see, still waiting for my host to clear things up. But in the past we just kept dist/ and vendor/ in origin too and never had different things on different remotes and I never experienced this issue with deleted files sticking around on prod then so I might just try the tag idea and see if it works. Thanks for your help – powerbuoy Jan 29 '20 at 08:10

1 Answers1

1

if the deleted file is in the new .gitignore, it'll definitely not be deleted as your commit wouldn't look at that file at all.

If it's and you still want it to be deleted, you'll have to push to production using your original .gitignore files, then continue with you're 'Sleek Deploy'

jalsh
  • 801
  • 6
  • 18
  • No it doesn't have to do with the gitignore switch. That works fine because I do steps 2 and 3. This just happens if I simply add any random file, push it to origin and production, then later on decide to delete the file, push the deletion to origin and production, it will only be removed from origin - not production. It was never in any of the gitignores. – powerbuoy Jan 28 '20 at 10:41
  • How do you get files from your bare repository into the actual production folder? It must be something wrong with that script/command then, but that's not git. – Lasse V. Karlsen Jan 28 '20 at 11:12
  • I'm not sure how they do it tbh. But OK at least I can take that with me to the WPEngine support. – powerbuoy Jan 28 '20 at 11:20
  • But how come it works fine when I do a normal `git push production master` where all remotes are up to date and on the same commit? But not when I do a "stealth" commit, push that to production and then revert said commit? – powerbuoy Jan 28 '20 at 11:22