1

I aim to add a rudimentary change log to the top of every file, e.g.:

# Changelog: 
# Last Modified on: 31.2.2020
# Last Modified by: Arthur Dent
# Last Modification: "After a Trillian tries, it works"
# File created: 01.01.1970

def whale2pot(args) ....

Now, in order to not have to do that manually, I would like to include the output of

git log -1

to the files concerned by this commit. (Not sure whether including the commit message is smart though..)

One way of doing this is via a bash script, which prepends the output of the above to the file(s). But this modifies the file(s), and the repo would never be actually up to date.

Hence: Is there a way to "overload" git commit or somehow sneak this in without git noticing?

Thanks in advance =)

Martin
  • 21
  • 4
  • I wonder if a pre-commit hook would do the trick. – Greg Burghardt Jul 01 '20 at 17:46
  • Related: https://stackoverflow.com/q/384108/3358272 – r2evans Jul 01 '20 at 17:57
  • You can't use the information from the last commit _because_ that will change the content of the files which will required a _new revision_ to be created to save this modification. – eftshift0 Jul 01 '20 at 18:26
  • It's certainly doable, I just don't have the right idea to do it without turning the repo into a merging nightmare, though. – LeGEC Jul 01 '20 at 21:49
  • @GregBurghardt, thanks for introducing me to git hooks. I will try it with a pre-push hook, as i would want to prepend output from the last commit. I'm guessing it'll be a challenge though, if i am pushing multiple commits. ¯\_(ツ)_/¯ – Martin Jul 02 '20 at 10:35

2 Answers2

1

It can be surprisingly hairy to have such an obvious source of merge conflicts in your repo,
and it is very easy to extract the info from git while you browse your repo (git log -1 the/file).

Before digging into how to actually store that info in the file's content, perhaps you could settle for a handy shell shortcut, or for an outline integrated to your editor ? vscode for example has the git lens extension which gives you something pretty close (per line annotations, actually)


The creation date would be pretty static, so it could be inserted at a file's creation and kept that way ;

for the other parts of the header : I think a filter would be the closest to the right way to do it

Official docs here : gitattributes

See an explanation in this SO answer : Can git filter out certain lines before commit?

You would write two scripts :

  • one, the clean script, would replace the header lines with constant values (eg : # Last Modified : with no date)
  • the second, the smudge script, would run git log -1 and fill the lines with the desired values

The clean script will be run when staging a file, and would make sure that the blobs stored in git have a constant header, to avoid problems when merging, rebasing, etc

The smudge script will be run when checking out a file, and will write the correct content in the worktree version -- the file on disk, which you would actually open in your editor.


The main point not sorted in this answer is : the smudge script receives the file's content on stdin, not the file's name, so I don't see a clean way to run git log -1 file/name from that script yet.

LeGEC
  • 46,477
  • 5
  • 57
  • 104
  • 1
    "*[insert link to relevant @VonC answer here]*" The link is still missing! :-) – phd Jul 01 '20 at 23:01
  • @phd DO you mean https://stackoverflow.com/a/59336862/6309? But don't forget to use Git 2.27+: https://stackoverflow.com/a/60905975/6309 – VonC Jul 02 '20 at 03:54
  • @VonC : I was thinking about a simplere explanation :) About the content filters : you get access to these information only if you set a long running filter process (`filter.[driver].process`), right ? no way to plug them in a smudge script ? – LeGEC Jul 02 '20 at 07:49
  • @LeGEC I usually am only using smudge/script, not long running filters described in https://git-scm.com/docs/gitattributes#_long_running_filter_process – VonC Jul 02 '20 at 08:45
  • Thank you all **big time**, the internet is a wonderful place! <3 I'll check this out. @LeGEC, regarding the filename, [this Stack Exchange answer](https://unix.stackexchange.com/questions/422951/how-do-i-take-a-filename-in-the-command-line-as-an-argument.) seems to do the trick. Will keep you posted. – Martin Jul 02 '20 at 10:18
  • @LeGEC Albeit accepting my own answer, as I posted the code, I wanted to thank you for pointing me the direction! – Martin Jul 09 '20 at 10:45
1

After some tinkering, I chose this format (here it is for .m files):

%% ------------------------------------------------  
%  
% Created on:       <date_of_creation>  
% Author:           <author>  
%  
% Last modifier:    <modifier>  
% Last modified:    <date_of_last_mod>  
% On Branch:        <branch>  
%  
%%-------------------------------------------------

... and realized that I don't need the commit meta-data to achieve my goal. I get:

  • the dates from $(date)
  • the Author/Modifier from $(git config user.name)
  • the current branch from $(git rev-parse --abbrev-ref HEAD)

Still I followed @LeGEC's approach of having one script which does two things, as follows:

  • insert_changelog.sh: This ensures that every untracked file with a specific extension receives a changelog. Further, it fills the static information of <date_of_creation> and <author>.
  • update_changelog.sh: This script updates the latter 3 fields of every tracked and modified file.

For the time being, I run this manually before running git add <modified files>.

I append the code below. This is my first bash scripting experience so feel free to point out what could be improved <=)


insert_changelog.sh:

#!/bin/bash

#If there are no .m files for which this would apply, suppress the this notification (If i get that right)
shopt -s nullglob

#Act on untracked files 
files=($(git ls-files --others --exclude-standard))
for item in ${files[*]}
do
    #For time being, only consider matlab files
    if [[ $item == *.m ]]
    then
        #Check whether the header already exists
        read -r first_line < $item 
        first_cl_line="%% ------------------------------------------------"
        if [ "$first_line" = "$first_cl_line" ]
        then
            continue
        else
            #Include Changelog into file
            cat changelog_template.txt > tempfile
            cat $item >> tempfile 
            mv tempfile $item

            #Fill in static fields of inception date and author
            sed -i "3,4d" $item
            sed -i "2 a % Created on:       $(date)" $item
            sed -i "3 a % Author:           $(git config user.name)" $item

            #Update current changelog
            ./update_changelog.sh $item
        fi
    fi
done;

update_changelog.sh:

#!/bin/bash
USER=$(git config user.name)  
BRANCH=$(git rev-parse --abbrev-ref HEAD)  
#Remove outdated lines and replace with updated ones.  
   for item in $(git ls-files -m)  
   do  
       sed -i "5,8d" $item  
       sed -i "5 a % Last Modifier:    $USER" $item  
       sed -i "6 a % Last Modified:    $(date)" $item  
       sed -i "7 a % On Branch:        $BRANCH" $item  
       sed -i "8 a %" $item  
   done;
Martin
  • 21
  • 4