Home > Net >  Does Git have builtin logging of command output?
Does Git have builtin logging of command output?

Time:09-11

Can Git log its command output ? (or do I have to write my own tee wrapper ?)

A general ability to enable logging for specific commands and subcommands would be handy for debugging purposes. The user should not have to remember to toggle logging after it is configured.

Or other use case: The output of git fetch can be later retrieved for an high level summary of what changed since the last fetch.


Desired example:

If the following command is executed:

$ git fetch
remote: Enumerating objects: 6, done.
remote: Counting objects: 100% (6/6), done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 6 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (6/6), done.
From /root/tmp/repo1
   f1a7f65..c8027d7  branch1    -> origin/branch1
 * [new branch]      branch2    -> origin/branch2
 * [new tag]         tag1       -> tag1
$

then log output to file:

$ cat .git/logs/command/fetch
2022-09-10 17:57:45 - git fetch
2022-09-10 17:57:45 - remote: Enumerating objects: 6, done.
2022-09-10 17:57:46 - remote: Counting objects: 100% (6/6), done.
2022-09-10 17:57:47 - remote: Compressing objects: 100% (3/3), done.
2022-09-10 17:57:47 - remote: Total 6 (delta 0), reused 0 (delta 0)
2022-09-10 17:57:48 - Unpacking objects: 100% (6/6), done.
2022-09-10 17:57:48 - From /root/tmp/repo1
2022-09-10 17:57:48 -    f1a7f65..c8027d7  branch1    -> origin/branch1
2022-09-10 17:57:48 -  * [new branch]      branch2    -> origin/branch2
2022-09-10 17:57:48 -  * [new tag]         tag1       -> tag1
$

Potential dirty workaround:

git() {
    logfile=`git rev-parse --git-dir`/git.log
    [ -w $logfile ] && command git "$@" 2>&1 | tee -a $logfile
}

CodePudding user response:

The short answer is no: Git is a traditional Unix / Linux style command, meant to be operated by a human at a shell prompt, and such commands traditionally don't log anything. Command wrapper tricks can work, but a more general way to deal with this is to run everything inside a "save all terminal interaction" command such as script. Consider also the related programs (with different goals) screen and tmux.

Note that Git changes its behavior depending on whether stdout and/or stderr are "tty devices" per isatty(). Using a command wrapper to redirect output through tee in a pipeline may change the commands' behavior. There are various workarounds for this, such as interposition libraries (see Is there a way to set up a Linux pipe to non-buffering or line-buffering?) but script uses a pty.

There is of course a tradeoff between "save everything" (e.g., script) and "save only the Git output" (a wrapper that logs).

CodePudding user response:

No, there's no builtin logging facility. What you can do is create a shell function named git which wraps all your git calls and redirects their output (stdout stderr):

git() {
  command git "$@" 2>&1 | tee -a logfile.log
}

Use exactly like regular Git commands, e.g. git log

The function could be extended to write different log file (for stdout, stderr), append/overwrite, use different paths, etc.

NB. The function shown here will convert any output on stderr to output on stdout, making it difficult to use it in other pipelines or redirections. In other words, with this function it is not possible to run git … 2>/dev/null, because stderr will always be empty.

CodePudding user response:

Are you searching for the git reflog?

git reflog

Example output:

a32556a HEAD@{0}: commit: migrating content
ab371fd HEAD@{1}: commit: adding git reflog outline
23a491a HEAD@{2}: checkout: moving from stage to feature/solver
7b119cb HEAD@{3}: checkout: moving from feature/solver to stage
56a183a HEAD@{4}: commit: changing color scheme
7a2aa71 HEAD@{5}: commit: adding more color palettes
a56322b HEAD@{6}: commit: adding color tool package

CodePudding user response:

This seems to work ok (beware knittl's and torek's caveat about redirection):

~/.bash_profile:

gitCmdLogger() {
    local cmd="command git $*"
    if echo $cmd | egrep '(fetch)|(merge)|(push)|(pull)' &> /dev/null; then
        if local logDir=`command git rev-parse --git-dir 2> /dev/null`/logs; then
            if [ -w "$logDir" ]; then 
                local logfile=$logDir/git.log
                echo "--- `date` - $cmd" >> $logfile
                eval $cmd 2>&1 | tee -a $logfile
            else
                eval $cmd
            fi
        else
            eval $cmd
        fi
    else
        eval $cmd
    fi
}

git() {
    gitCmdLogger "$@"
}

Nb. You will have to escape your quotes eg. git commit -a -m \'a commit\'.

Example output:

$ cat .git/logs/git.log
--- Sat, Sep 10, 2022 10:46:49 PM - command git fetch
From /root/tmp/repo1
   74f552b..f541e62  branch2    -> origin/branch2
 * [new branch]      branch3    -> origin/branch3
--- Sat, Sep 10, 2022 10:48:43 PM - command git pull
Your configuration specifies to merge with the ref 'refs/heads/master'
from the remote, but no such ref was fetched.
--- Sat, Sep 10, 2022 10:48:49 PM - command git merge
merge: refs/remotes/origin/master - not something we can merge
$

Here is the script I used to test it (Nb. it should be run via source as it uses aliases):

#!/bin/bash

alias g='git'
alias ga='g add'
alias gci='g commit'
alias gco='g checkout'

rm -rf repo[12]

mkdir repo1
cd repo1
g init

cd ..
g clone repo1 repo2

cd repo1
gco -b branch1
echo `date` > file1
ga file1
gci -a -v -m \'file1.\'
g tag tag1

gco -b branch2
echo `date` > file2
ga file2
gci -a -v -m \'file2.\'

cd ../repo2
g fetch

cd ../repo1
gco -b branch3

gco branch2
echo `date` >> file2
gci -a -v -m \"file2 more date.\"

cd ../repo2
g fetch
g merge
g fetch
g pull

cat .git/logs/git.log
  •  Tags:  
  • git
  • Related