Reputation: 143
I have a local dev server where I do all my changes, we'll call this local
.
I have a git repo setup in a git folder on my webserver
, we'll call this gitfolder
.
And then I have my live files, that are being pushed from gitfolder everytime I git push, we'll call this live
.
My workflow.
Edit files, commit changes, push to webserver
. Then a post-update takes over with the following:
#!/bin/sh
export GIT_WORK_TREE=/path/to/live
git checkout -f
This works perfectly. However, here lies my question. I want to revert a git change, and I would normally do this via git reset --hard commit
- but I if I navigate to live
, obviously that's not a git repo.
When I go over to gitfolder
and run the same command git reset --hard commit
it doesn't update live
and I get the error 'fatal: This operation must be run in a work tree'
Steps to take? For now, I've gone and copied my local
folder and made a local2
- and on local2
I reverted the change, then pushed that to live
, so my original local
folder still has all the changes.
I DO NOT WANT TO REVERT local
- Just on live
Upvotes: 0
Views: 214
Reputation: 488143
You probably don't want to change any refs in the server either (git reset
would move whatever branch HEAD
points to, probably master
, but you said you don't want to revert or reset your local system, so based on that, I assume you don't want to revert or reset your bare repository either).
In that case, just log on to the server, cd
to the bare repository, and run:
git checkout --work-tree=/path/to/live checkout -f <commit-ID>
or:
GIT_WORK_TREE=/path/to/live git checkout -f <commit-ID>
In other words, you're just doing what the update hook does, but with a raw commit-ID to extract that particular version, without changing any branches in the repository itself.
While I think that's the literal answer to your question, I'd like to point out something else: if you git revert
the bad commit in your own local repository, you get a new commit that undoes the effect of the bad commit, but the bad commit is still in there. You can then push the result to the server, in the usual way:
... - o - X - o - U <-- master, origin/master
where X
is the bad commit and U
is the commit that "un-does" what X
does. Just for illustration I've included an uninteresting o
commit in between them, too.
Now you can simply revert your revert, so that your local repo ends with the "bad" commit being replayed yet again:
... - o - X - o - U <-- origin/master
\
X' <-- master
where X'
is a copy of the bad commit, which "un-does" the undoing that U
did. You can now fix it up, make another commit on top or git commit --amend
, etc., and when it all works, git push
the result to the server again. That would be a more typical way to deal with the issue.
Based on comments, here is another option, which may or may not be suitable, better, etc: instead of resetting or reverting locally, make a new local branch or tag (let's use branch here) that points to the commit you want restored on the server. You may also want a name on the server, so that it won't garbage-collect commits (which would mean you would have to send them over the network again later).
For instance, let's say the tree on local
looks like this, drawn with a kink just so that I can add a label:
... - o - R
\
X - o - o -...- o <-- HEAD=master, origin/master
Here R
is the commit you want the server to reset-to, while X
and the additional commits are the ones that update Wordpress plug-ins and so on. (I'm assuming you're working on branch master
; change as needed.)
Meanwhile, over on the server, things look like this:
... - o - R
\
X - o - o -...- o <-- HEAD=master
If you want to retain all the commits on the server, we should give the final o
commit a new branch-name, since we'll have to force-update master
there. So on local
, we might run this:
$ git push origin master:save
This will create a new branch on the server, called save
, so that it now looks like this:
... - o - R
\
X - o - o -...- o <-- HEAD=master, save
The update
hook will do the usual git checkout -f
, which checks out the HEAD
branch (since no branch is specified), which in this case is master
, so the server is updated to the last o
commit (pointlessly, it's already there). But next, we do this, again on local
:
$ git branch for-server <commit-ID-for-R>
This changes the setup on local
to look like this:
... - o - R <-- for-server
\
X - o - o -...- o <-- HEAD=master, origin/master
Not very interesting yet, but next:
$ git push --force origin for-server:master
This (with --force
) tells the server to forcibly update its master
to point to commit R
, after which it has this:
... - o - R <-- HEAD=master
\
X - o - o -...- o <-- save
That save
label keeps the remaining commits in the repository on the server. Meanwhile the post-update
hook runs and does a git checkout -f
, which uses HEAD
, which points to master
, which points to commit R
. So the web server should now have commit R
deployed.
Back on local
, you just need to remember that for-server
maps to master
on the remote. (If you like, you can rename all your local branches to match the naming on the server: change master
to save
and change for-server
to master
, for instance. That's all independent of this.)
Note that the only thing save
is doing on the server is keeping commits X
and all the subsequent o
s. That's nice if you want to work on the server directly, or don't want to send those commits back over the net. However, if, on the server (which has a work-directory since it's not a bare repo), you a git checkout save
, you'll change HEAD
to point to save
and the next time the post-update
hook runs, it will deploy the save
version, rather than the master
version.
Upvotes: 3