Be Present Now

durdn is here talking about tech stuff. or so he says.

Git Tips And Workflows, Round 2: basics, stashes and other bits

You'd think I fired all my bullets with my previous round of curated tips, but no, I have plenty more.

The new year has just kicked off and I am happy to be back with more findings, shortcuts and workflows to enhance your git experience. I also humbly anticipate the optimizations and corrections that you guys will feed me back. I very much appreciate that.

Before I dive in the next round let me also give you a second to open a tab for my older post on more advanced git aliases, if you haven't seen it already.

Ready? Alright here we go, starting from the simple things this post will cover:

Table Of Contents

  1. Find the root of the project
  2. Delete a remote branch
  3. Add all unknown files to a repository while honoring .gitignore
  4. Show all the content in your stashes
  5. Track all remote branches as local branches
  6. Git cherry to see which commits have been merged to a branch
  7. Check deleted files consistency between branches
  8. Bonus Tips On Submodules

Find the root of the project

Say your build script needs to know the root of your git repository. Here rev-parse to the rescue:

git rev-parse --show-toplevel

Which results in:

git rev parse

To automatically cd into it you can of course type:

cd $(git rev-parse --show-toplevel)

Delete a remote branch

This used to be more obscure before git v1.7.0. Now they improved the UI and it's fairly straightforward:

git push origin --delete <branchName>

Easy enough. (Stack Overflow reference)

Add all unknown files to a repository while honoring .gitignore

Yes yes, we've all done git add * to add everything that is unknown to the index and the next commit. This works well until you have a well crafted .gitignore for your project. At that point git will halt and show you the following warning:

git add all

And yes you can force the issue with the -f flag, but then you lose the benefit of having a .gitignore file in the first place and you will add files that you don't want to track.

So here's how you add to your repository all unknown files, but respecting the .gitignore and not raising any alarms (Update: Helpful readers have have pointed out a much simpler way in the comments, which I report here):

git add '*'

Or simply:

git add -A

Previously I had done it like this:

git ls-files --other --exclude-standard | xargs git add

Which results in the right files being properly added:

git add all

st is my alias for git status.

Show all the content in your stashes

I use the git stash command a lot in different situations. It is a very convenient way to temporarily save work which either is transient, or incomplete and thus should not be committed. For example I might use a stash to save a work in progress when I have to switch to the production branch to resolve a blocker. Or to reapply a temporary change I need often, but can't commit.

If you use stashes a lot, you might have hard time remembering what's in them, whether you have named them properly or not.

So you you type git stash list, and for example you get this output:

git stash list

What was in stash@{2} again? Or even better: where did I save that fix I was working on last Thursday?

So the following one-liner shows you the diff content of all your stashes. I found it quite useful (I indent it for readability but it can all go on one line or in one bash function):

for stash in $(git stash list | awk '{print $1}' | sed -e 's/://'); do 
  (echo $stash && git stash show -p $stash);
done

The result will allow you to see the contents of all your stashes:

git stash show

Track all remote branches as local branches in a project

It is not uncommon that you want to track all the remote branches of a project with local branches. I had this need several times. So I found this short command on - surprise - Stack Overflow:

for remote in $(git branch -r | grep -v master); do 
  git checkout --track $remote ;
done

It will create all the local branches needed and make sure they are tracking the remote ones.

Git cherry to see which commits have been merged to a branch

During the past year I worked with a huge Subversion repository for which I acted as Branch/Integration Manager. Obviously I used the fantastic bridge git svn for the task - for which I wrote a well received post last year. It worked wonders and accelerated my work a great deal.

A slight annoyance was that sometimes I had to resort on manipulating and cherry-picking single commits between branches.

One command I found useful is the lesser known git cherry (man page), which given two branches lists which commits have not been merged upstream.

Here for example how you list commits present in master but not present in develop branch:

git cherry -v --abbrev develop master

For example check out the output:

git cherry list

The interesting feature of the cherry command is that

git cherry compares the changeset rather than the commit id (sha1), you can use git cherry to find out if a commit you made locally has been applied upstream under a different commit id.

Check deleted files consistency between branches

As mentioned above, for a while I worked in an environment where everyone used Subversion but me. This created some interesting challenges and my normal git superowers sometimes had to be enhanced by random Unix cleverness.

One time I had to do a thorough consistency check to make sure all files that had been deleted in the master branch, had also been deleted in the release branch. Here's how I did it.

First I collected all the delete actions in master:

git checkout master

git log --all --pretty=format: --name-only --diff-filter=D | sort -u > deleted.txt

Then I verified that those delete actions really corresponded to deleted files (some files might have been re-added later):

for f in $(cat deleted.txt); do if [ ! -f $f ]; then echo $f; fi done > really-deleted.txt

And then I listed files that existed in the release-branch while they were gone in master:

git checkout release-branch

for f in $(cat really-deleted.txt); do if [ -f $f ]; then echo $f; fi done > problem.txt

The file problem.txt contained the files that were not deleted.

Bonus Tips On Submodules

As an extra bonus for you dear reader I want to point you to my latest post on useful workflow and tips for git submodules on Atlassian's Blog. Those are things you will run into if you ever work with them at all.

Conclusion

This ends the second round of my accumulated tips. I have a few more in store. If you'd like to be notified of my next posts you can or sign up to be notified by email below.

comments powered by Disqus