Git Inside Out

Pieces from a comprehensive Git book.

Git Cookbook

Delete a branch

git branch -d <branch> will delete a branch “safely” – it will only delete a branch if its entire contents have been merged into its upstream branch (if there is an upstream branch), or in HEAD if no upstream has been set. These are the two most common cases for branches, but it’s possible that you can have merged a branch and git is unaware because you’re on an unrelated branch. Note that if the branch has been merged to the upstream but not to HEAD, you’ll get a warning, but the branch will still be deleted. This warning is presumably because you might then go to delete the tracking branch in the upstream location without realizing that it’s not been merged to its final destination.

git branch -D <branch> will delete a branch even if this action can cause you to lose commits. The commits will stay in the repository until it is garbage-collected or repacked, but it will be hard to find them, because the branch’s reflog is deleted along with the branch.

To delete a remote branch with Git 1.7.0 or later, do git push <remote> --delete <branch>. For versions before 1.7.0, you do something slightly non-obvious but logical : git push <remote> :<branch> will delete <branch> from the <remote> repository. This is because the full syntax for push is git push <remote> <localbranch>:<remotebranch> and so the “delete” command is pushing nothing, which has the effect of removing the branch.

Push a branch

git push <remote> <branch> pushes the changes from a local branch to the corresponding branch in the remote repository. This is a subset of the full functionality of git push. The push will fail if the remote branch’s head is not an ancestor of the current branch’s head.

git push -f <remote> <branch> will force the push to occur even if the remote branch doesn’t appear to be related to the current branch (the shorter -f is a synonym for –force). This is necessary when a rebase has happened locally, for example, or when a new local branch has the same name as a previous remote branch. The –force option is dangerous, which is why it is not the default, because you can cause the remote repository to lose commits.

Rename a branch

git branch -m <old-name> <new-name> will rename a branch from <old-name> to <new-name>, unless <new-name> already exists.

git branch -M <old-name> <new-name> will rename a branch even if it has to overwrite an existing <new-branch>.

Track a branch

git branch --set-upstream-to=<upstream> <branch> sets <branch> to track the upstream branch <branch>. When you create a branch from a remote branch, this is typically set up for you, but if you create a branch from a local branch, you may want to have it track an remote branch.

Git commands

git checkout

Create new branch master-temp starting from the HEAD of master:
git checkout -b master-temp master

git diff

By default, git diff shows you the changes between your working tree and the index – in other words, it shows you changes you’ve made that haven’t yet been staged for committing.

–cached|staged <commit>

Show the diff between the index and some commit. If no commit is specified, then it will default to HEAD. Most of the time, you want to show the diff against the last commit, so you won’t specify any commit. –cached and –staged are synonyms of each other.

git log

–graph

The –graph option shows a text-based graphical representation of branch creation and merging on the left-hand side of the output.

git log –graph -10 –oneline <revlist>

-p|u|-patch

-p, -u and –patch are all synonyms. This displays a diff for each commit, in a format suitable for use as a patch. The default uses the Myers diff algorithm, but multiple diff algorithms can be used. Git separates the idea of diffs stored in the repository for use in minimizing storage versus diffs used to track changes. This means that new diff algorithms or new tracking strategies can be added and are backwards-compatible with existing repositories. On the other hand, more work has to be done at log time, since sub-file history has to be determined each time a history-tracking command is issued. On the whole, this is still markedly faster than previous systems where storage-diff and history-diff were the same thing. It also allows for new tricks to be added, like “git pickaxe”, which will find when something was added or removed.

git merge

<stuff>

git rebase

Rebase is the other fundamental way to rejoin work from multiple branches. The best way to think about rebase is that it changes the parent of a branch; the common rebase operation moves a branch (a sequence of linear commits) from one parent to another. Rebase rewrites history, and it is one of the only operations that can remove previously committed nodes (the other being branch deletion).

Rebase is far from a rare operation; in fact, rebase is one of the fundamental tools that developers in active repositories employ.

Simple rebase

Assume you have a branch ‘feature’, assume you started from ‘master’ 5 commits ago, and assume you want to move your ‘feature’ branch as if it were started from the current master.

git checkout feature
git rebase master

This will move the commits from their original place, rewrite them as necessary, and then set the parent commit to the tip of master. This will produce entirely new commits for every point along ‘feature’, even if all that is changed is the commit objects themselves.

The set of commits in this example that are moved are the commits in the revlist

git log master..feature

(this is the set of commits reachable from feature that aren’t reachable from master, hence “new”). However, this can alter the contents of commits, and it will alter the commit records themselves. The action of rebase is to apply each commit in turn onto the new branch point, and, as with any merge, there can be conflicts. The resolution of conflicts is up to the user.

continuing from merge conflicts

Sometimes you’ll have merge conflicts when rebasing – it’s like any other merge, your changes could conflict with others’. Address the conflicts in whatever way you see fit, then contine the rebase with

git rebase --continue

rebase practices

When rebasing a long chain of development, it’s  better to squash before rebasing, rather than squash after rebasing. It’s common for a chain of development to do something, then undo it, and that can create multiple pointless conflicts when rebasing. It’s even better to just squash in general before rebasing, as that will keep your rebase conflicts thematic, and easier to track.

ours and theirs in rebase

In a rebase, ours and theirs are flipped from the meaning that rebase employs. The new branch being constructed is ours, and the working branch that is being replayed from is theirs. Keep this in mind when using git checkout –ours and git checkout –theirs to select or the other side. If you want to keep “your” file in the conflict, you’ll actually use –theirs, and if you want to keep the file from the parent, you’ll use –ours. This is correct, even if it seems flipped, because –theirs refers to what is being merged from, and –ours refers to what is being merged into.

in a rebase

git checkout –ours path/to/file picks a file in the branch you are grafting onto
git checkout –theirs path/to/file picks a file in the branch you are rebasing

in a merge

git checkout –ours path/to/file picks a file in the branch being merged into
git checkout –theirs path/to/file picks a file in the branch being merged from

This could benefit from some pictures.

git remote

git remote add <remote> <path> to add a remote.

git remote remove <remote> to delete a remote.

git reset

Move the current branch back in time by 6 revisions (this will orphan these commits, unless some other branch points to them; use the reflog to recover them before your repository is repacked):
git reset HEAD^6

git shortlog

This is a built-in helper that summarizes git log output; it’s a synonym for git log –pretty=short.

git shortlog -s -n shows just the number of commits that each author made. Use it in conjunction with .mailpmap to group commits together (e.g. the same commiter used several different email addresses).

Github

Github is quickly becoming as dominant in source-control hosting as Google did for search or Facebook did for online social. Github exposes some Git features in a cross-repository fashion. It also has an enterprise version where companies can run their own private version of Github.

Search

You can search across the primary branch of all repositories at the same time. This is incredibly powerful if you want to see how often a feature is used, or if you want to find which project actually implemented some old feature you barely remember.

http://github/search

is the basic interface, and it’s a lot like Google in that it defaults to a single field that can still access most of the functionality. Now, that said, it does have some limits. It won’t search forks unless the fork has more stars than the parent, although I think you can force this by using a fork:true qualifier. It only searches the default branch in a repository (which is the master branch by default, although a repository owner can change this).

As well as searching code, you can search repositories (name, description, size, language, number of forks, etc), issues and users.

Git Preferences

Where does Git store preferences? There are three levels searched in this order

git config --local --list
git config --global --list
git config --system --list

System preferences are machine-wide and apply to all users and all repositories on the system. Global preferences are user-wide, and apply to all repositories accessed by that user. Local preferences apply to a single repository.

Local preferences are always in <project>/.git/config. However, global and system preferences have operating-system-specific locations.

Git does use some preferences for external systems, most specifically SSH.

Windows

Global (user-level) preferences are in %USERPROFILE%/.gitconfig, which will typically be C:\users\<username>\.gitconfig, but could be pointed to something else depending on your computer or network setup (e.g. some corporate networks point user directories to a network share for easier management and backup).

System (machine-level) preferences are in the Git installation folder, in <git-install>/etc/gitconfig. Windows git install puts files normally found in /etc in this location. This could be C:\Program Files (x86)\Git. There will be few files named gitconfig on a Windows system, so you could just search for it, but Git is very likely in %PATH%.

SSH prefs go in ~/.ssh – your public and private keys should go here, although sometimes (old versions of git?) they need to go in <program files>/Git/.ssh. The OpenSSH key file will be called id_rsa and id_rsa.pub.

Git Bash

The ~ directory, environment variable $HOME (and should match %USERPROFILE%). SSH keys in ~/.ssh.

Linux and Mac

Global/user preferences are in ~/.gitconfig.

System/machine preferences are typically in /etc/gitconfig.

Reference

Rebase and merge

http://git-scm.com/book/en/Git-Branching-Rebasing

http://gitready.com/intermediate/2009/01/31/intro-to-rebase.html

https://www.atlassian.com/git/tutorial/rewriting-git-history#!rebase

https://help.github.com/articles/interactive-rebase

http://stackoverflow.com/questions/804115/git-rebase-vs-git-merge

http://rypress.com/tutorials/git/rebasing.html

Merge conflicts

http://stackoverflow.com/questions/161813/how-do-i-fix-merge-conflicts-in-git

http://weblog.masukomi.org/2008/07/12/handling-and-avoiding-conflicts-in-git

http://git-scm.com/book/en/Git-Branching-Basic-Branching-and-Merging

http://githowto.com/resolving_conflicts

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>