Reputation: 1033
I have a setup on a server containing a bare repository that checks out code to a working directory when code is pushed to it.
I had to edit some files directly in the working directory on the server. Thus, those changes are not committed into the bare repository and I can't pull them to my local machine.
Here is what I tried (and what I didn't)
I tried searching for this on StackOverflow but couldn't get the answer. Maybe not using the correct GIT terminology since I'm just learning GIT.
Upvotes: 2
Views: 319
Reputation: 490178
DileepNimantha's answer is mostly correct (it's missing a step, at least at the time I write this), but there are several potentially-easier ways. We'll get to the easiest one later; let's start out with what we're really doing. I'll add that the missing step is to run git fetch
and git read-tree
(see below) after git remote add ...
. There's also an assumption that the current branch master
is correct.
The fundamental issue is the one you've identified: you have a bare repository (with core.bare = true
, git init --bare
, etc.) which therefore has no work-tree.
To make a new commit in any Git repository, you technically don't need a work-tree at all. What you need is for your index (also known as your staging-area or sometimes your cache) associated with that repository to have the correct file contents. The standard way to do this, though, is using git add file1 file2 ...
, which copies the given files from your work-tree into your index, overwriting the old versions of those files in the index. This of course does need a work-tree.
A bare repository, despite having no work-tree, still has an index. Here's how we can bypass the lack of a work-tree.
You can fill the index from a specific commit, such as the tip of master
, using git read-tree
:
$ git read-tree master
You can then replace one of the index entries from a file in the file system. In this case, I updated sig.py
by extracting it to a file in the file system that is now in /tmp, and then edited it there:
$ git hash-object -w -t blob /tmp/sig.py
b6a42a1dd3fff08aa4bed6b054b310f69ed518c1
It's now necessary to produce the git ls-files
stage entry for that file. The old one is:
$ git ls-files --stage sig.py
100644 7e49ad4f1042b14e088571a091a7b9f7d1010b4c 0 sig.py
We don't need to use the stage number since git update-index
allows us to skip it, so we can print the new hash with the rest of the appropriate information directly:
$ printf '%s %s %s\t%s\n' 100644 blob \
b6a42a1dd3fff08aa4bed6b054b310f69ed518c1 sig.py | \
git update-index --index-info
I've split this up into three lines, two with backslash-newline, for posting length purposes. It's really all one command, but when expressed this way as shell input, it will work just as well. Note that the new blob hash is the one we got by writing the new updated file into the repository. (Side note: we have about 14 days to finish our work once we start this process, before git gc
declares our newly-created blob object to be unused, and therefore prunes it.)
We now need to write the index as a tree object:
$ git write-tree
e92275076a62ca850b0bda3f6c3603632b4a6cce
and then store this tree as a commit object. Let's make this a new commit that we'll add after the current tip of master
:
$ git commit-tree -p master \
-m 'make signal handler work in py2k and py3k' \
e92275076a62ca850b0bda3f6c3603632b4a6cce
Running this produced, for me, the commit hash:
e068bdfce2fd992dc396cb4969327ef5c4d39a43
We now need to make some branch name, perhaps a new one, refer to this commit hash. I'll use a new one rather than updating master
in place:
$ git branch fix-signal e068bdfce2fd992dc396cb4969327ef5c4d39a43
We can now see whether this worked by running git log
in this bare repository:
$ git log --oneline --decorate -n 2 fix-signal
e068bdf (fix-signal) make signal handler work in py2k and py3k
11ae6ca (HEAD -> master) add run-checks script
Let's view the change as a patch:
$ git show fix-signal
[snip]
index 7e49ad4..b6a42a1 100644
--- a/sig.py
+++ b/sig.py
@@ -5,7 +5,10 @@ import sys
import time
def signal_handler(signum, frame):
- os.write(1, bytes('caught sig %d\n' % signum, 'iso8859-1'))
+ if str is not bytes:
+ os.write(1, bytes('caught sig %d\n' % signum, 'iso8859-1'))
+ else:
+ os.write(1, 'caught sig %d\n' % signum)
def catch_by_sig():
old_int = signal.signal(signal.SIGINT, signal_handler)
But this really is a --bare
repository:
$ git status
fatal: This operation must be run in a work tree
The git
front-end command takes many options that appear before the verb. For instance, if your extracted work-tree on the server lives in /tmp/work
, you can, on the server, cd
to your Git repository:
$ cd ...
and then run:
$ git --work-tree=/tmp/work status
and get status output. Note that git status
compares the HEAD commit with the current index contents, and then the current index contents with the work-tree. Since your main Git repository here is a --bare
repository, its index might not match the contents of your /tmp/work
directory at all. (Depending on how you have your server deploy pushes, the index for that Git repository may be non-empty. You must decide whether you care what is in it.)
If necessary, you can use a git read-tree
operation, just as we did in the longer form above, to fill the index from some specific commit. You can name the commit by its branch name, as I did above, or by its hash ID: the key is to fill the index from the commit that's currently deployed into the deployment-area.
Now that the index is filled in a way to match (mostly) the deployment area, except for the modified files, you can run:
git --work-tree=/tmp/work add file1.ext file2.ext
for instance, to add those files from the work-tree (you will do this while "cd"-ed into the Git repository). You can then run:
git --work-tree=/tmp/work commit
as usual. Note that this will commit on the current branch, i.e., the one that HEAD
is attached to in the repository. That's probably correct because of any deployment script you're using.
Essentially, each time you run a Git command, you tell it: ignore the core.bare
setting, there is a work-tree over in this other path. In fact, that's probably how your deployment-during-push-operation hook works.
I don't recommend doing this as normal practice—you're getting all the cost of allowing pushes to a non-bare repository, with none of the benefits, so you might as well just set up a non-bare repository instead of doing this kind of mucking-about. But as a quick hack around an emergency, it works.
Upvotes: 1
Reputation: 224
Of course you can commit your code from your working directory. You should simply initiate this as a git repository. Try following:
git init
git remote add origin <your git url>
git add
your changes and commit and push to your repository.Upvotes: 0