7

I'm a git newbie suffering from prior svn experience. Many of my projects are using code from my own libraries, so naturally I want some "externals-like" functionality from git. I'm currently trying to use submodules.

But submodules (as far as I understand) can provide a lot of pain if you use them in the wrong way (for example, change files in submodule and forget to push them, or forget to commit them).

What if I make all files in submodule read-only? That's a good enough protection from accidental change. And if I really want to change something I should go and make that change in the original repo.

So, my question is:

  1. Is that a good idea or I'm trying to reinvent the wheel?
  2. What's the easiest way to do it?

EDIT: I hope that could be achieved with git hooks, but I'm not sure how exactly. I can't use client hooks when cloning the repo for the first time, can I?

EDIT2: With help of SRobertz I was able to come up with a post-checkout hook:

echo "you just checked out: $*"  
echo "submodules:"
for p in `grep path .gitmodules | sed 's/.*= //'`; do # get submodules list
    echo "making submodule directory $p read-only"
    chmod -R a-w $p;
    SAVEIFS=$IFS
    IFS=$(echo -en "\n\b") #set delimeter to \n\b to handle whitespaces in filenames
    for f in `ls $p`; do #get files in submodule dir
         echo "making file $f in directory $p read-only"
         chmod -R a-w $p\\$f;
    done
    IFS=$SAVEIFS #restore delimeter to default value
done 

Now the problem is that when cloning a new repo, this hook fires too early, when submodule directories are already created but files in them are not pulled yet.

Amomum
  • 6,217
  • 8
  • 34
  • 62
  • I think the easiest way to do it is what you suggested: don't edit files in submodules; just edit the original repository and then update your submodules. I wouldn't bother trying to make things read-only. Just don't edit them. – larsks Feb 14 '15 at 23:10
  • I have actually edited them accidentally. Also I'm working with some people who may ignore alerts from git. So making files readonly would be handy. – Amomum Feb 15 '15 at 00:11
  • Client-side hooks are client side, so each user has to set them up. But you can automate/package that. See, e.g, http://stackoverflow.com/questions/427207/can-git-hook-scripts-be-managed-along-with-the-repository, http://stackoverflow.com/questions/3462955/putting-git-hooks-into-repository. Some discussion of why you have to install hooks on client side: http://git.661346.n2.nabble.com/Is-there-any-way-to-make-hooks-part-of-the-repository-td7518033.html| But if you have users who "may ignore alerts from git", submodules may not be for you as it requires discipline, also for updating. – drRobertz Feb 15 '15 at 21:09
  • @SRobertz, can you please give me a hint, exactly what hooks I need to set up? The whole point of making submodules read-only is to make breaking things more difficult without much discipline. If something will not compile locally - that's okay, as long as remote repo stays intact. – Amomum Feb 15 '15 at 21:55
  • Then, another option is to do something on the remote side to deny pushing. – drRobertz Feb 15 '15 at 22:06
  • @SRobertz, well, I can make all submodules remote links RO, but this would not protect people from pushing their code with detached head, pointing to local anonymous branch. I'm not sure about my terminology here. I.e. they can still commit their changes in submodule and then push everything else. – Amomum Feb 15 '15 at 23:07
  • You may find something useful in http://stackoverflow.com/questions/4763687/what-is-the-best-way-to-write-a-git-update-hook-that-rejects-invalid-submodule-c https://gist.github.com/bagage/bdca3d4b66d43db7a5e3 That said, if you don't need the stability given by submodules, I'd look at alternatives – drRobertz Feb 16 '15 at 03:44
  • I edited my answer to include a bit on how to use a client-side hook. – drRobertz Feb 16 '15 at 10:20
  • "... submodules (as far as I understand) can provide a lot of pain if you use them in the wrong way ...". So can a meat cleaver. The trick is simply to learn to use them the right way. They really aren't that bad, unless you really don't want to learn and live by a few additional rules/practices. – twalberg Feb 16 '15 at 20:00
  • the problem is the rules/practices are not intuitive so revisiting an old project usually requires the hassle of relearning them – malhal Jun 11 '19 at 11:55

1 Answers1

2

If you want svn-like externals you may want to look at giternal, https://github.com/patmaddox/giternal , either to use as-is or as a starting point. It is written in ruby, and should be fairly easy to adapt to what you want if the freeze command is not quite what you want (wich it perhaps isn't).

Short intro: http://www.rubyinside.com/giternal-easy-git-external-dependency-management-1322.html

A longer article with some alternatives at the end: https://codingkilledthecat.wordpress.com/2012/04/28/why-your-company-shouldnt-use-git-submodules/

Edit: on client-side hook and its deployment: If you are aware of the quirks of git submodules and want to use that (perhaps for the stability you get from referencing a specific commit rather than HEAD) then this is a way to make it read-only when cloning or checking out.

The client-side hook to use is post-checkout, and iterate over the submodules, along the lines of (inspired by List submodules in a git repository, and git-clone and post-checkout hook. I used grep on .gitmodules as it works before the submodule is inited.)

#!/bin/sh
# An example post-checkout hook to make submodules read-only

echo "you just checked out: $*"
git submodule init
git submodule update --recursive
echo "submodules:"
for p in `grep path .gitmodules | sed 's/.*= //'`; do
    echo "making submodule directory $p read-only"
    chmod -R a-w $p;
done    

Then, to deploy the hook, one option is to set up a template directory for your developers and then make them do git clone --template=</your/template/dir> url-to-clone... (e.i., add the --template=... option to git clone, perhaps by making it an alias and somehow putting that in everybody's global config.

EDIT2: after discussion in comments:

If submodules have been made read-only, they need to be made writable before updating (according to comments, git on windows does this automagically, but it is required on linux/macos). To do that on a git pull, a post-merge hook like the following sketch can be used.

#!/bin/sh
# An example post-merge hook to 
#   1. make submodules writable,
#   2. init and update submodules, and 
#   3. make them read-only

# make all (existing) submodule directories writable
for p in `git submodule status | sed -e "s/^[+\ ][^\ ]*\ //" -e s/\ .*$//`; do
    echo "making submodule directory $p writable"
    chmod -R u+w $p;
done

echo "updating submodules:"   
git submodule init
git submodule update --recursive

# make all submodules read-only
for p in `grep path .gitmodules | sed 's/.*= //'`; do
    echo "making submodule directory $p read-only"
    chmod -R a-w $p;
done

This could be refined to check if any submodule was updated in the merge and only handle that, instead of always iterating over all submodules.

Caveat: how this interacts with git pull --recurse-submodules needs to be checked if you intend to use that.

Note that this does init and update all submodules, and make them read-only, after each merge (pull).

It does not, however, address doing git pull etc. inside the submodules. For that, the corresponding hooks need to be added to .git/modules/*/hooks (for git versions >= 1.7.8) or <submodule>/.git/hooks (for older git versions).

Community
  • 1
  • 1
drRobertz
  • 3,490
  • 1
  • 12
  • 23
  • I'm a bit puzzled why client-side hook to use is post-update. All I was able to find was "This hook is invoked by git-receive-pack on the remote repository, which happens when a git push is done on a local repository". So that's a hook that fires after a push? I gave it a try though and wasn't being able to find an action that should fire it. – Amomum Feb 16 '15 at 12:18
  • Oops, sorry, it should be *post-checkout*. Will edit. – drRobertz Feb 16 '15 at 12:20
  • That's almost it! One problem with your script is (on windows at least) that making folder read-only doesn't actually mean anything; I still can modify files in it. So I have to change your script slightly to adress this issue. However, with this modification it doesn't work properly when cloning - post-checkout fires before submodule files are pulled. Any ideas? I'll edit the question to include the script. – Amomum Feb 16 '15 at 15:39
  • Oops again. The previous version actually did prevent updating the submodules, as it made the directories read-only. :-) Answer updated – drRobertz Feb 16 '15 at 17:23
  • ... and the script in the answer is tested and works (for a small example) on Linux and MacOS. – drRobertz Feb 16 '15 at 18:10
  • ... but it still feels a bit brittle. – drRobertz Feb 16 '15 at 18:20
  • Well, apart from the fact that in windows you still have to make files read-only (not only the directory they are in), that does work for cloning and checkout! The drawback is - this hook doesn't fire when you make 'git submodule update' or 'git pull' inside of the submodule. There aren't any hooks for these, are they? – Amomum Feb 17 '15 at 17:03
  • The hook triggered by a `git pull` is `post-merge`, so you could insert that (and post-checkout for submodule update) in under each directory in `.git/modules/` (which is the .git dir for the submodules). But then you also need to make all the submodules writable before the `git pull` or `git submodule update`, or you will get a permission denied. You could script that or make aliases but it feels very brittle. Did you look at giternal? Or the server-side hooks for checking commits i linked in my 2nd last comment on your question? – drRobertz Feb 17 '15 at 20:03
  • BTW, the -R flag to `chmod` means (on *nixes) recurse into directories, so it does change the permissions of the files as well (if the ordering of the commands is right, i.e. do the submodule init and update before the chmod per my edit.) But I don't know about windows. I don't know if I can provide any more assistance. but if I can, we should probably move the discussion to chat. – drRobertz Feb 17 '15 at 20:08
  • Okay, thanks a lot! On windows git seems to ignore read-only flag when updating or pulling, so that's not a problem. – Amomum Feb 17 '15 at 22:08