0

I'm a Perforce veteran who is moving our organization over to git. One thing I'm stuck on is how to manage large commits (I'm not referring to many different changes at once -- which I know isn't optimal -- but rather, a single type of change that affects many files at once). For example, let's say I add a parameter to a function that is called in dozens of files. Using the git command line with a visual diff tool, what's the best way to review all of my changes before committing? In practical terms I might spread this review out over a period of time, or might make a few more changes after reviewing some of my files, then get back to the unreviewed files.

In short, what I'm looking for is a way to easily difftool files one at a time and then mark them as reviewed. I know there are many UI solutions out there, but I'm interested in a native git solution.

 

Rejected ideas:

  1. Keep all files unstaged, then diff them one at a time, and stage a file if I like the diff. The problem with this idea is that it's cumbersome to type out the full paths of every file to do the diff (and then to stage it), since files may be sprinkled across many paths.
  2. Use interactive add mode (git add -i). The problem with this idea is that you can't diff unstaged files. Meaning that for this to work I would have to do some weird gyrations where I'd stage everything, then after a file passes my review I would actually unstage it, then at the end I'd swap the staged and unstaged files before commit. Gross.
  3. Use patch mode. This is very cool, but doesn't seem to work with a visual difftool.
  4. Use git gui. This one is actually really close, but I haven't found a way to easily run difftool on each file with a double-click or hotkey.

 

Ironically, git add -i would work perfectly for my needs if it didn't reverse the meaning of diff. i.e. by default, diff works on unstaged changes, but during an interactive add, diff only works on staged changes. This is perplexing to me, especially since the point of git add is to stage files.

All thoughts are appreciated! Thank you for your help.

Kevin Meboe
  • 143
  • 1
  • 6
  • Sounds like you are trying to take advantage of the "staging area". Once you think something is ready to be committed, you add it to the index..... when you are "ready" (like, added all the files you think are ready to be committed), you commit. What is on the staging area will be committed, what's not in there, won't be committed, – eftshift0 Feb 28 '19 at 21:25
  • @eftshift0 that's right. The difficulty is in reviewing, say, a couple dozen files at once, and staging each one as they pass review. – Kevin Meboe Feb 28 '19 at 21:27

1 Answers1

3

You should abandon the idea of commits as used in non-distributed scm systems... git add -i and variants are what you are looking for:

  • pick your changes with 'git add -i' and commit them; do not try to be perfect and do not hesitate to do multiple commits

  • for new files, I would commit an empty file first (perhaps with some headers); you can do this one-by-one in your editor:

    1. empty the file (throw away your work)

    2. git add the-file + git commit -m "the-file: initial checkin"

    3. undo step 1

Repeat steps above; when you get some experience in it, you can use 'git commit --fixup HEAD~2` or create commit messages like "fixup! added parameter xyz" or "squash! fixed stupid bug".

There are some tools which help with git add -i; e.g. git-gutter+-mode in emacs does a nearly perfect job and the magit c f sequence is really cool.

Now, you have history like

added parameter xyz
the-file-A: initial checkin
fixed stupid bug
the-file-B: initial checkin
fixup! added parameter xyz
fixup! added parameter xyz
squash! fixed stupid bug
...

Now, git rebase -i --autosquash to your master branch, reorder commits and you will get:

the-file-A: initial checkin
the-file-B: initial checkin
added parameter xyz
fixed stupid bug

where the latter two commits contains changes over multiple files/places.

Every of these commits is about a single problem and can be reviewed.

You can run compile and unittests with git rebase -f -e make.

ensc
  • 6,704
  • 14
  • 22
  • Thank you for your time and effort...+1 for that. There are some new things in your post that I need to research (like --autosquash). Can you tell me more about abandoning the idea of Perforce-style commits (other than frequent commits, which I do understand)? If I perform many small commits without reviewing the code each time, doesn't that just push the problem down the road? Then when I merge it seems like I'll have a more monolithic task (reviewing the entire merge). This is why I'm thinking it's best to review before each commit. – Kevin Meboe Mar 01 '19 at 00:19