Git - How to find all "unpushed" commits for all projects in a directory?

git log
git branch
git grep
git find file
git reset
git commands
git show
git commit

I'm finally getting used to Git and, after the initial steep learning curve, I must say it's quite good (I just miss the single file externals, but that's another story). I have, however, an issue that I can't solve: I'm currently working on a dozen projects at the same time. They are all interconnected and I must jump from one to the other, making changes here and there.

All good so far, I can "juggle" them quite easily, I commit changes regularly and so on. The issue is that, after several hours, I don't remember which projects I pushed to the remote repository. Of course, I can "cd" into each project's directory and issue the push command, but it's tedious. I was wondering if there would be a way, using bash, to run something like a git find all unpushed commits in all projects in this directory. Such command would be run from a directory which is not a Git repository, but which contains a tree of all the projects.

The logic is simple, but the implementation seems quite complicated, also because, although the root directory contains all the projects, the actual Git repositories can be found at any level from the starting directory. For example:

  • projects directory
    • customer X
    • project1 (Git repo)
    • customer U
    • project2 (Git repo)
    • project3
      • somelibrary (Git repo)
      • theme (Git repo)

In this example, only directories in bold are Git repositories, therefore the check should be run inside them. I'm aware that the structure looks a bit messy, but it's actually quite easy to handle.

Any help is welcome, thanks.

You can find unpushed commits with git cherry, so you should be able to write a bash script like

#!/bin/bash
for file in `find ./ -type d -maxdepth 3` ; do
    cd $file
    git cherry -v
    cd -
done

which will find 3 layers of subdirectories, but it's probably not very nice to look at.

EDIT

As Evan suggests you can use a subshell to avoid cd-ing back a directory, like so

#!/bin/bash
for file in `find ./ -type d -maxdepth 3` ; do
    (cd $file && git cherry -v)
done

You might want to put a pwd command in here somewhere to see which file/directory you're in...

Viewing the Commit History, As you can see, this command lists each commit with its SHA-1 checksum, the further limits the output to just those commits that match all --grep patterns. This question is closely related to List all commits for a specific file however it is different. I want to find out which commits, across all branches, had modified a given file. To make it more complex, the given file may or may not be in the working tree.

You could have a look at git submodules but personally I don't find them pleasant to work with.Some time ago I thus created a script to work on a collection of git repos: https://github.com/mnagel/clustergit

clustergit allows you to run git commands on multiple repositories at once. It is especially useful to run git status recursively on one folder. clustergit supports git status, git pull, git push, and more.

git-show-branch Documentation, git show-branch [-a|--all] [-r|--remotes] [--topo-order | --date-order] [--current] Arbitrary extended SHA-1 expression (see gitrevisions[7]) that typically names a  git-tree is preferred because it's a plumbing command; meant to be programmatic (and, presumably, faster) (assuming base branch is master) git diff-tree --no-commit-id --name-only -r master..branch-name However this will show you all files which were affected in the branch, if you want to see explicitly modified files only, you can use --diff-filter:

Make the top level a git repo and make each project a submodule. Now you can run

git submodule foreach --recursive git status # or any other command

recursive is not needed if you don't have any submodules within your projects.

git-ls-files Documentation, One or more of the options below may be used to determine the files shown: If any <file> does not appear in the index, treat this as an error (return 1). In reality, you’re unlikely to use that often, because Git by default pipes all output through a pager so you see only one page of log output at a time. However, the time-limiting options such as --since and --until are very useful.

You can use the following one-liner, which you can use to search from a directory which is not a Git repository, as you requested. This will list the absolute paths to the git repositories which have unpushed commits:

find . -type d -iname '.git' -exec sh -c 'cd "${0}/../" && git status | grep -q "is ahead of" && pwd' "{}" \;

This works by recursively searching for .git directories, and when found cd to the parent directory, which is the git project root. Then run git status and grep for the "is ahead of" phrase. If this is true the command after && is executed, in this case the 'pwd' command. It is here where you can insert you own commands (for example to show the unpushed commits).

Note: only works for unpushed commits on the active branch

List all developers on a project in Git, To show all users & emails, and the number of commits in the in the list (find a list of unique committer and author), you could use git log : git rev-list --simplify-by-decoration -2 HEAD this gives me just two SHAs: 1) last commit of the branch [C1] 2) and commit parent to the first commit of the branch [C2] git log --decorate --pretty=oneline --reverse --name-status <C2>..<C1> Now I get a list of full history of file changing within the branch

Add this alias to your .gitconfig file and then you can run git status-all from a parent directory to get the status of all git repositories recursively.

[alias]
status-all = "!for d in `find . -name \".git\"`; do echo \"\n*** Repository: $d ***\" && git --git-dir=$d --work-tree=$d/.. status; done"

See also this post.

How to Do a Git Log Search, Suppose I wanted to search for the string "facebook" in all of the commit messages of my project. All it takes is this one command: git log --grep="facebook​". That creates a directory named libgit2, initializes a .git directory inside it, pulls down all the data for that repository, and checks out a working copy of the latest version. If you go into the new libgit2 directory that was just created, you’ll see the project files in there, ready to be worked on or used.

git ready » list remote branches, -a shows all local and remote branches, while -r shows only remote So, once you know the name of the branch it's quite simple to check them  With just about any size codebase, you’ll often need to find where a function is called or defined, or display the history of a method. Git provides a couple of useful tools for looking through the code and commits stored in its database quickly and easily. We’ll go through a few of them.

Git - List all files currently under source control?, You can also specify HEAD instead of master to get the list for any other branch you might be in. If you want to get a list of all files that ever existed, see here: git log  git, Git branches, ls-remote This tutorial will help you to list remote branches available on the remote git repository. It is helpful you to find names of branches, which have been created on the remote repository by someone and you want to check out this on your local repository.

Advanced Git Log, Inspect a repo's history with git log: format how a commit is displayed, filter which commits are included in the output to find any information you need. git ls-files --unmerged and git ls-files --stage can be used to examine detailed information on unmerged paths. For an unmerged path, instead of recording a single mode/SHA-1 pair, the index records up to three such pairs; one from tree O in stage 1, A in stage 2, and B in stage 3.

Comments
  • Instead of using cd - to change to the previous directory, you could just make the loop's body a subshell.
  • Does this work for all branches or just the active branch?
  • @DanStevens unfortunately not, just tested. Tried to find a way of showing only unpushed commits on remote tracking branches but haven't found a correct way yet.