We saw in the previous section how git records which commit each ref is pointing to. Each time a ref is changed to point to a new commit, a record of that change is made, so that each ref has a history of which commits it has pointed to. There's more information about this here.
These can be useful sometimes e.g. when a commit becomes unreachable and orphaned[1]Perhaps because you reset HEAD incorrectly., and while it might look like work has been lost, the objects are still there in the repo[2]Orphaned objects will be kept for some time, until they get cleaned up during garbage collection., and by examining the reflog, you can reset a ref to bring those objects back into play.
Logs are stored in .git/logs/, one file per ref, and are straight-forward.
The object names are before and after values, a value of all 0's means that there is no value, the message after the timestamp appears to be optional.
This is some code that locates each log file:
def read_logs(): """Find and read logs in a repo.""" # look for log files logs = [] dname = ".git/logs" for root, dirs, files in os.walk( dname ): #pylint: disable=unused-variable for fname in sorted( files ): # found one - extract the ref name fname = os.path.join( root, fname ) assert fname.startswith( dname ) ref = fname[ len(dname)+1 : ].replace( os.sep, "/" ) # read the entries in the log entries = _read_log_file( fname ) logs.append( ( ref, entries ) ) return logs
And some code that parses a single log file:
def _read_log_file( fname ): """Read entries from the specified log file.""" with open( fname, "r", encoding="utf-8" ) as fp: entries = [] while True: # read the next line line = fp.readline().rstrip() if line == "": break # parse the next entry mo = re.search( r"^([0-9a-f]{40}) ([0-9a-f]{40}) (.+?) \<(.+?)\> (\d+) ([+-]\d{4})(\s+[^:]+)?", line ) if not mo: print( "ERROR: Couldn't parse log line:" ) print( " {}".format( line ) ) sys.exit( 1 ) prev_ref, next_ref, user_name, user_email, tstamp, tzone, entry_type = mo.groups() msg = line[mo.end()+2:] if mo.end() < len( line ) else None # nb: this is optional # save the entry entries.append( { "type": entry_type.strip() if entry_type else entry_type, "refs": ( None if prev_ref == "0"*40 else prev_ref, None if next_ref == "0"*40 else next_ref ), "user_name": user_name, "user_email": user_email, "timestamp": parse_timestamp( tstamp, tzone ), "msg": msg } ) return entries
Source code
A new script logs.py will dump the logs in a repo.