Tag: git

git add -p

I have auto-formatting and linting configured in my text editor. It’s great for automatically fixing most code format issues and I highly recommend it. However, when working with legacy codebases, this can make for some messy diffs.

I’ll usually try to disable auto-formatting if I anticipate an issue, but sometimes I’m not expecting it or forget. Rather than manually reverting the changes, it’s usually easier to stage the changes I want, commit, and then reset the unstaged changes.

To interactively choose which parts I want to stage, I use git add -p.

From the manpage:

-p --patch Interactively choose hunks of patch between the index and the work tree and add them to the index. This gives the user a chance to review the difference before adding modified contents to the index. This effectively runs add --interactive, but bypasses the initial command menu and directly jumps to the patch subcommand. See “Interactive mode” for details.
Code language: plaintext (plaintext)

When you run the command, you’ll initially see the first part of the patch with a prompt for how you want to handle it.

(1/55) Stage this hunk [y,n,q,a,d,j,J,g,/,s,e,?]?
Code language: plaintext (plaintext)

From the Interactive Mode docs:

y - stage this hunk n - do not stage this hunk q - quit; do not stage this hunk or any of the remaining ones a - stage this hunk and all later hunks in the file d - do not stage this hunk or any of the later hunks in the file g - select a hunk to go to / - search for a hunk matching the given regex j - leave this hunk undecided, see next undecided hunk J - leave this hunk undecided, see next hunk k - leave this hunk undecided, see previous undecided hunk K - leave this hunk undecided, see previous hunk s - split the current hunk into smaller hunks e - manually edit the current hunk ? - print help
Code language: plaintext (plaintext)

After staging all the relevant hunks, you can commit and push as normal. Then git reset --hard resets the other pending changes.

The GitHub Desktop app as similar functionality to commit parts of a given change using a GUI interface.

Automate git bisect

I occasionally use git bisect to figure out where I’ve introduced test failures. If you’re not familiar, given known good and bad commits, along with a test command, git bisect does a binary search over your repo to determine where a bug or test failure was introduced. This is especially useful on large repos with a test suite that takes a minute or two (or more ?) to run.

Most of the time, I just need to check the last handful of commits. To that end, I’ve written a bash function that assumes the current HEAD is bad, 10 commits prior is good, and automatically runs a test command that you define.

gbi() { git bisect start git bisect bad git checkout HEAD~10 git bisect good git bisect run "$@" }
Code language: JavaScript (javascript)

This won’t cover every case, but should help automate this fairly verbose process most of the time.

Git essentials for SVN users

Coming from Subversion, the extra steps involved in working with a Git repository can be confusing. Here are some basic concepts and commands to get started with Git.

I find that actually doing something is the best way for me to understand. If you’re the same way, the Github tutorial by Code School is great.

Concepts

There are a couple conceptual differences between Git and Subversion that it helps to understand before getting started.

Centralized vs Distributed

Subversion is centralized version control — every commit lives on a central server. Git is distributed version control — everybody has a copy of the repository. When you commit to subversion, the push to the central server is implicit. The commit and push are discrete actions in Git. This means you can do work and commit changes without necessarily being connected to the Git server. Another advantage is the ability to make commits locally without necessarily publishing your work.

File vs Change-based version control

Subversion is file-based — when you make changes and run svn commit, it commits all the changed files by default. Git is changed-based — when you make changes, you have to run git add to “stage” changes. You can get similar behavior to Subversion with git commit -a. The “a” flag tells Git to add all changes in tracked files.

Commands

  • init – Create a new repository.
  • clone – Set up a repository that already exists on a remote server.
  • add – Stage changes to be committed.
  • commit – Add changes to the local repository.
  • push – Push unsynced commits to a remote repository.
  • pull – Pull unsycned commits from a remote repository.

Basic Workflow

  1. Get the latest version of the code. If you already have the repository, you can use git pull to get the latest changes. Otherwise, git clone will download the code from a remote repository and set it up locally. If this is a new repository, you can set it up with git init.

  2. Make changes.

  3. Share your changes. git add <file>; git commit; git push or git commit -a; git push.