Reputation: 1020
We are transitioning a project to replace some CSS files with SASS.
During development when working with .scss
files, .css
files are generated automatically, but we don't want these committed in the repository.
Say we have a module named loginModule
which resides in the folder login
. Inside login
we currently have these two CSS files: login.css
and ux-theme.css
. I want to replace login.css
with login.scss
. When pushing my code, I should not be able to push login.css
if login.scss
exists.
What would be the best way to ensure that a .scss
and a .css
file with the same names do not exist in the same directory?
Upvotes: 0
Views: 352
Reputation: 488845
You have asked specifically about a server side hook, i.e., a pre-receive or update hook, but of course, there are at least two sides here: the server, and the client(s). On the clients, the job is, or at least can be, easier, because you can deal with ordinary files in an ordinary file system. The (currently only) other answer by 0.sh does this kind of thing, though not as a hook, and not even using any actual Git commands: it merely adds .gitignore
entries, and if a file is already tracked, these entries will do you no good anyway.
I will address instead the server-side question you asked, since the server is the only place you can enforce a total ban. The problem is that pre-receive and update hooks are hard to write.
Whichever kind of hook you choose, remember that you are called either with arguments on the command line, or a series of input lines on stdin, giving you the ref-name (for update
: one reference, as arguments) or complete set of references (pre-receive
: all refs, as input stream) to be updated. Your job is to verify that you agree to the proposed update(s), and exit with a zero status if so, or a nonzero status if not.
I illustrated the mechanisms for doing this in a pre-receive hook in this sample that checks for merge commits. In your case, you might do something similar, but replace the function check_branch_add_commits
so that instead of checking for merges, it checks the added commits' contents. Remember also to modify check_branch_update
to handle the "remove some commits, add other commits" case as well here. When some commits are being removed, they are in the output from git rev-list $NEW..$OLD
; when some commits are being added, they are in the output from git rev-list $OLD..$NEW
. (Since check_branch_add_commits
checks those, you could simply call it for all cases.)
Next, in order to check the added commits, you must iterate through them one by one:
local rev
for rev in $(git rev-list $OLD..$NEW); do
check_one_commit $rev
done
where check_one_rev_commit
checks the specified commit.
Your test boils down to "if name.scss exists, forbid name.css from existing" in some set of (all?) directories, so check_one_commit
must examine the specified commit ($1
will be its hash). This command:
git ls-tree -r --name-only $1
produces a list of every file in the commit. File names with embedded spaces are simply listed, but if a file name has an embedded newline, it gets encoded:
$ git ls-tree -r --name-only HEAD
foo
has * star
"has a \nnewline"
name with a space
You may wish to handle this differently, or use the -z
option when you write a program to read through the names.
Next, you must write a program to read through the names. :-)
This program will be rather complicated. You do get one nice guarantee: all names within each sub-directory will be "clustered" into that directory, e.g., this is a partial git ls-tree -r --name-only
from a real repository:
rfuncs.c
rfuncs.h
sbuf/sbuf.c
sbuf/sbuf.h
transport/socket.c
transport/socket.h
utils.c
The two sub-directories here are sbuf
and transport
, and as you can see, they are listed after all "r"-named files and before any "u"-named files, in sorted order.
Your program's task, then, will be to read the list of files from stdin and exit 0 (success) if there are no directories containing any *.scss
file with a corresponding *.css
file, or nonzero if there are. (If you use -z
you can read through each ASCII NUL character.) You can then do this:
if git ls-tree -r --name-only -z $rev | /path/to/program/to/check; then
# do nothing, this commit is OK
else
echo "bad commit found at $rev"
STATUS=1 # assuming my general framework
fi
You might have your checking program print why the commit is bad (i.e., the full path names of the scss and css files that are conflicting).
There is still the small matter of writing this program, but I leave that to you. If your program reads standard input, it will be easy to test: simply prepare a list of "proposed git ls-tree
outputs" and run it on each one, making sure that it either rejects that tree, or accepts it, depending on whether that tree meets your particular criteria.
Upvotes: 1