Is there a trick for git log --follow directory that has been renamed?

The git command has a useful command for viewing a file after renaming, as in git log --follow path/to/some/file . Unfortunately, it only works for a single file. I would like to make the equivalent of git log --follow some/directory .

One way to do this would probably be to massage the output of git ls-tree and do it in a loop, but then a problem arises that affects multiple files, will not be "merged" into a single transaction.

Is there a better way to do this? Note: using git 2.7.4

+7
git
source share
2 answers

Not.

Git does not store directories, only files. When you use any path name for commands such as git log or git diff that look at entire commits, Git essentially says "start with the whole commit, and then shorten it to files matching that path." The directories here simply complete the selection of each file in the directory.

The --follow can follow only one file. Therefore, if you can somehow force it to apply to a directory, Git will first turn the directory into a set of files, then select one of these files and follow just that.

(The actual --follow code --follow horribly hacked. It uses a rename detection code, but only works when commits are compared in a later order: if you add --reverse , --follow never actually, you need to throw it all over and re-encode it Perhaps recoding it, you can force it to process several file names and even directories full of files.)

+4
source share

There seems to be no built-in way to do this.

This can be done using a script and after a simple algorithm:

  • For each file, a list of related commits is created.
  • Combine all the lists and make them stable sorting (this is difficult, because determining the order of commits is not easy)
  • Iteratively execute a git log for each of the commits and pass it all through

Here's a hacky way to do this with python 3 and the sh module will not work on Windows for various reasons.

 #!/usr/bin/env python3 import os import shlex import sys import tempfile import sh def _get_commits(fname): # Ask for the commit and the timestamp # .. the timestamp doesn't guarantee ordering, but good enough for c in sh.git('log', '--follow', '--pretty=%ct %h', fname, _tty_out=False, _iter=True): c = c.strip().split() yield int(c[0]), c[1] def git_log_follow_multi(filenames): if len(filenames) == 0: print("Specify at least one file to log") elif len(filenames) <= 1: os.system('git log --follow -p %s' % filenames[0]) else: # Use git log to generate lists of commits for each file, sort commits = [] for fname in filenames: commits += _get_commits(fname) # Sort the lists (python sort is stable) commits.sort(reverse=True) # Uniquify (http://www.peterbe.com/plog/uniqifiers-benchmark) seen = set() seen_add = seen.add commits = [c for c in commits if not (c in seen or seen_add(c))] # Finally, display them tname = None try: file_list = ' '.join(shlex.quote(fname) for fname in filenames) with tempfile.NamedTemporaryFile(mode='w', delete=False) as fp: tname = fp.name for _, commit in commits: fp.write('git log -p -1 --color %s %s\n' % (commit, file_list)) # Use os.system to make our lives easier os.system('bash %s | less -FRX' % tname) finally: if tname: os.unlink(tname) if __name__ == '__main__': git_log_follow_multi(sys.argv[1:]) 

Now this script does not meet your needs, as it accepts a list of files, but you can execute it with glob and it will do what you are looking for.

 ./script.py src/* 
0
source share

All Articles