# Git notes Here are some notes I find handy for using git. * Looking at stuff * __ o See a log of commits. => git log --graph --decorate --stat # Good compromise git log --graph --decorate --stat -p # To see detailed diffs git log --graph --decorate --oneline # For longer histories <= o See the origin and branches of a working copy: => git config --get remote.origin.url git remote show origin git config -l git branch -a -v <= o See the unique hash for any file: => git hash-object --no-filters filename <= o See if a file ``Foo.java'' is being tracked, maybe just staged for a commit => git ls-files -s | grep Foo.java <= o See all ways to specify a revision: https://git-scm.com/docs/gitrevisions o Find the ten biggest files in your repo => git ls-tree -r -l --full-name HEAD | sort -n -r -k 4 | head -n 10 <= o List files in a bare repository. => git ls-tree --full-tree -r HEAD <= _ * Setup * __ o Make a bare copy, and add as a new remote so that you can push to it. => cd repo # go to repository git clone --bare `pwd` /backup/repo.git # make bare copy git remote add backup /backup/repo.git # give it a name git push backup # for updates git remote -v # see new remote <= o Add upstream (if doesn't already exist) and track. => git remote add upstream https://github.com/USERNAME/repo.git git branch -u upstream/master master <= o Change an existing origin or upstream, and track master. => git remote set-url origin $HOME/Dropbox/git/repo.git git branch -u origin/master master git remote set-url upstream /path/repo.git git branch -u upstream/master master <= o Set an upstream for current branch the first time you push => git push -u origin [current_branch] <= o Improve performance on NFS: => git config core.preloadindex true <= o Mirror a repo: => git clone --mirror repo_url git fetch --all # to update <= o For better diffs by default: => git config --global diff.algorithm patience <= _ * Fixing stuff * __ o Redo the last commit, before having pushed => git commit -m "Bad commit" git reset HEAD~ # keeps changes as uncommited, unless --hard [edits] git remove [stuff] git add [stuff] git commit -c ORIG_HEAD # Reuses previous commit message; otherwise git commit # Make new commit message <= o Edit commit message after commiting and before pushing. => git commit --amend <= o Undo a merge. (Try to avoid using --hard for any resets.) => git reset --merge ORIG_HEAD <= o Revert a pushed commit. => git revert 123ab... git commit <= o To revert just some files in a pushed merge. => git revert --no-commit 123ab... git reset git add path/file.txt path/directory/ git commit git reset --hard <= o Remove a directory from git without affecting local working copy. => git rm --cached -r some_directory/ <= o To undo all commits and edits that have not yet been pushed to the origin. This is like deleting your working copy and checking out again. => git reset --hard origin/master <= o Make a detached HEAD point to master You have done ``git checkout rev'' to look at a revision at some point, so HEAD now points somewhere different from master (or your current branch). You want to point HEAD back to master. => $ git checkout master $ git log --decorate --oneline a047b24 (HEAD -> master) later revision ... <= If you have any changes to save, then first stash them, and apply later. Otherwise, you will be obliged to destroy them with ``git checkout -f''. o Making master point to a detached HEAD. You have done ``git checkout rev'' to look at a revision at some point, so HEAD now points somewhere different from master (or your current branch). You want to move master to where HEAD currently is. If you have any changes to save, then stash them, and apply later. Otherwise, you will be obliged to destroy them with ``git checkout -f'' and ``git reset --hard''. In this case the HEAD points to a later revision than master: => $ git log --decorate --oneline a047b24 (HEAD) later revision 6e0bc6b (master) earlier revision <= First, make the HEAD point to master => $ git checkout master $ git log --decorate --oneline 6e0bc6b (HEAD -> master) earlier revision <= Next move both back to the later revision => $ git reset a047b24 $ git log --decorate --oneline a047b24 (HEAD -> master) later revision 6e0bc6b earlier revision <= Let's begin again with a detached HEAD at an earlier revision, so master is not visible: => $ git log --decorate --oneline 6e0bc6b (HEAD) earlier revision <= First, make the HEAD point to master => $ git checkout master $ git log --decorate --oneline a047b24 (HEAD -> master) later revision 6e0bc6b earlier revision <= Now you can move both back to where HEAD used to be => $ git reset 6e0bc6b $ git log --decorate --oneline 6e0bc6b (HEAD -> master) earlier revision <= o Get file contents at a particular revision. You want to get some files as they looked after a particular commit, but the commit may not have directly affected those files. First you find the commit hash you want with => git log --stat <= and you find a commit ``4e7e2793026f'' on November 17, a moment at which you liked a particular file, even though that commit did not change that file. You can cat the contents of a file ``FT.java'' at that revision two ways => git show 4e7e2793026f:./FT.java git cat-file -p 4e7e2793026f:./FT.java <= Use full paths, or use a dot for a path relative to the current directory. To find the hash of the blob at that particular moment => $ git ls-tree -r 4e7e2793026f | grep FT.java 100644 blob 23df3f17a4af6cc9a55907cc9273729911193b97 FT.java <= To cat the contents of that file blob: => git show 23df3f17a4af6 git cat-file -p 23df3f17a4af6 <= o List all references: => git show-ref <= o Change the active branch on a bare repository => git symbolic-ref HEAD git symbolic-ref HEAD refs/heads/mybranch <= o Delete a file from history (read about BFG elsewhere): => git clone /foo.git cd foo/ java --jar bfg.jar -D big_ugly_file.ppt git reflog expire --expire=now --all && git gc --prune=now <= o You have just attempted a pull from a bad repository and received the following message: => From /path/to/garbage_repo * branch HEAD -> FETCH_HEAD hint: You have divergent branches and need to specify how to reconcile them. hint: You can do so by running one of the following commands sometime before hint: your next pull: hint: git config pull.rebase false # merge hint: git config pull.rebase true # rebase hint: git config pull.ff only # fast-forward only hint: You can replace "git config" with "git config --global" to set a default hint: preference for all repositories. You can also pass --rebase, --no-rebase, hint: or --ff-only on the command line to override the configured default per hint: invocation. fatal: Need to specify how to reconcile divergent branches. <= If you want to reconcile the merge, then do as suggested. But maybe this repository really is garbage and has filled up your ``.git'' repository with some giant worthless blobs that you want removed. Or perhaps it is a completely unrelated repository. Your ``.git/FETCH_HEAD'' file will contain the commit at the top of the the garbage repo. You can see this commit and its history with ``git show FETCH_HEAD'' and ``git log FETCH_HEAD''. ``FETCH_HEAD'' is the only actual reference. You can simply delete this file, or you can call ``git fetch'' to replace it with the ``HEAD'' of your current origin. You must remove this reference before you can remove the garbage it points to. Next empty your reflog and garbage collect: => git reflog expire --expire=now --all && git gc --prune=now --aggressive <= Your ``.git'' repo should have removed all the dangling alien objects. If you now attempt to examine the previous ``FETCH_HEAD'' commit with ``git show [commit]'', it will fail as a "bad object." _ * Branches * __ o Make a new branch and preserve local changes without changing master. Then commit and push to origin. Compare to master. => git checkout -b new_branch_name git commit -a git push --set-upstream origin new_branch_name git diff master..new_branch_name <= o Remove a branch, locally and remotely => git branch -d branchname git push origin --delete branchname <= o Remove local references to remote branches that no longer exist: => git branch -a -v git remote -v git remote prune origin for b in `git branch -vv | grep ': gone]' | awk '{print $1}'`; do git branch -D "$b" ; done <= o Track all remote branches => B=`git branch --list | grep '^[*]' | sed 's/^[*] //' ` git remote prune origin git fetch -p --all git branch -r | grep origin/ | grep -v '\->' | while read remote; do b="${remote#origin/}" if ! git rev-parse --verify "$b" > /dev/null 2>&1 ; then git branch --track "$b" "$remote" else git checkout $b git pull git checkout $B fi done git fetch -p --all for b in `git branch -vv | grep ': gone]' | awk '{print $1}'`; do git branch -D "$b" ; done git pull --all git branch -vv --all <= o Compare a file in two different branches: => git diff -b otherbranch myfile # compare to HEAD git diff -b branch1 branch2 myfile git diff -b branch1..branch2 myfile <= o Recover a local branch that you deleted too soon. => $ git checkout master Switched to branch 'master' Your branch is up-to-date with 'origin/master'. $ git branch -d foo Deleted branch foo (was 5698004). $ git reflog | head 51c4772 HEAD@{0}: checkout: moving from foo to master 5698004 HEAD@{1}: checkout: moving from master to foo $ git branch foorestored 5698004 <= o Merge specific file from another branch Here are two ways to do it. => git checkout master git checkout other_branch -- relative/path/to/foo.txt # [1] git show other_branch:absolute/path/to/foo.txt > absolute/path/to/foo.txt # [2] <= _ * Making a Subversion working copy into a Git respository * First check out a working copy of Subversion, and create a new git repository that shares the working copy: => svn co URL/trunk svnGitRepo cd svnGitRepo git init cat > .gitignore <<EOF .svn/ EOF git add . git status git commit -m "imported from svn" git log <= Then I can make a copy of this repository to a detachable disk. => cd /media/USBDISK git clone /path/svnGitRepo gitOnly # locally <= Here's a sample remote path: => git clone ssh://host/~/path/svnGitRepo gitOnly # remote <= Assume that the git-only copy is modified offsite. => cd /media/USBDISK/gitOnly echo foo > testfile git add testfile git commit -m test git log <= Later that detachable disk is reconnected and you want the changes merged with the latest version of the SVN repository. First update the original SVN working copy, and save the changes into that Git repository. => cd $HOME/svnGitRepo svn up git commit -m "updated from SVN" <= Thet get the updates from the offsite copy and check them into SVN. => cd $HOME/svnGitRepo git pull /media/USBDISK/gitOnly svn commit -m "updated from Git" <= Then merge those SVN changes into the offsite Git copy: => cd /media/USBDISK/gitOnly git pull <= If the automatic merge fails, then edit the file however you want. Then, add the conflicted file again, to stage it for commitment: => git add path/to/merged_file git commit -m "merged SVN updates" <= You can also ``push'' commited changes from the client back to the original repository, but I discourage it. Your changes will not immediately appear in the original working copy files. Bill Harlan, 2009-2016