Git rebase -i

Combine Multiple Local Commits into One

  # *hack*
  git commit -a -m"one"
  # *hack*
  git commit -a -m"two"
  git rebase -i HEAD~2
  # bring us an editor, select squash "two" as squash, save, exit.
  git log

Splitting Commits

In interactive mode, you can mark commits with the action edit. However, this does not necessarily mean that git-rebase expects the result of this edit to be exactly one commit. Indeed, you can undo the commit, or you can add other commits. This can be used to split a commit into two:

  • Start an interactive rebase with git rebase -i commit^, where commit is the commit you want to split. In fact, any commit range will do, as long as it contains that commit.
  • Mark the commit you want to split with the action edit. Save and exit.
  • Once get back to prompt, execute git reset HEAD^. The effect is that the HEAD is rewound by one, and the index follows suit. However, the working tree stays the same.
  • Now add the changes to the index that you want to have in the first commit. You can use git add (possibly interactively) or git-gui (or both) to do that.
  • Commit the now-current index with whatever commit message is appropriate now.
  • Repeat the last two steps until your working tree is clean.
  • Continue the rebase with git rebase --continue.

If you are not absolutely sure that the intermediate revisions are consistent (they compile, pass the testsuite, etc.) you should use git-stash to stash away the not-yet-committed changes after each commit, test, and amend the commit if fixes are necessary.

Git amend

An alternative workflow is to use git commit -amend. But this does not keep your micro history around. It just puts any recent changes into the last commit.

  # *hack1*
  git commit -a -m"I am working on blah"
  # *hack2*
  git commit -amend # will make hack1 hack2 as well.
  ...

If you have a tree which has four commits on top

  git-reset --soft HEAD^^^
  git-commit --amend

In order for git-reset --soft to leave the index in the desired state, no pending stuff should be in index before beginning this process (index is the thing controlled with cmds like git-add/git-update-index/etc.).

Git checkout

  # checkout a tag v2.1.0 (use git tag to show a list of tags)
  git checkout v2.1.0

Git diff

  # diff any changes to the index (i.e. modification to any previous checked in files but not yet added/staged).
  # but not including new files that has not been added
  git diff
  git diff --unified=10                # gives you 10 unchanged lines before and after changes (i.e. context for changes)

  # diff any added but uncommited (staged) files.
  # (both previously checked in or newly added, but not including new files that has not been added).
  git diff --cached
  git diff --cached ./somedir/         # you diff a dir
  git diff --cached ./somedir/file     # or diff some file

  # diff any changes to is and is not added to your index.
  # (changes since last commit added or not, but not including new files that has not been added).
  git diff head
  git diff HEAD

  # diff 2 revision back (any of the following will work):
  git diff head^^
  git diff head~2
  git diff head~2 somedir/file          # you can do a diff on specific file
  git diff head~2 somedir/              # or a sub directory
  git diff head~2 -- somedir/file       # Note you can use -- before file name as well.

  # diff two commits
  git diff head head~2 -- somedir/file  # diff head and two commits ago.

  # diff two different branches
  git diff head master~3 --stat         # files that has changed between current branch and master branch 3 commits ago.
  git diff ..master                     # changes that the master branch is missing (-)
  git diff master                       # changes that are added since master branch (+)

  # see only files (no diff patches)
  git diff head head~2 --stat           # shows you the file names that has changed & how much it's changed.
  git diff head head~2 --name-status    # shows you the file names and which that has been modified, deleted or added.
  git diff head head~2 --numstat        # number of lines added and removed from each file (added first).
  git diff head head~2 --name-only      # name only, nothing more.

  # patches 
  git diff ..master > master.patch      # this will diff from mater and create a patch that can be applied to current branch (notice the ..).
  git diff head master > master.patch   # this is the same as previous command.
  git diff ..master~3 > master.patch    # you can even get a patch from 3 revisions ago from master's head
  git apply master.patch                # patch current branch w/ master branch (like merge)
  patch -p1 < master.patch              # git apply is just patching a unified patch file. (same effect as above)

Git merge

Automatic Squashing Multiple Commits during merge.

You can squash all commits from another branch and merge them into the current one by specifying --squash option.

  git merge --squash my_branch

Git reset

  git reset --hard head~4  # permanent revert to 4 commits ago

Git revert

Use git revert to revert one commit and resubmit the patch as a new commit.

  git revert 2b5c813cf2a3a5539f43f7ccfa38db7d9e3d572c

Usually the command automatically creates a commit with a commit log message stating which commit was reverted. Use -n flag to apply the change necessary to revert the named commit to your working tree and the index, but does not make the commit.
In addition, when this option is used, your index does not have to match the HEAD commit. The revert is done against the beginning state of your index.
This is useful when reverting more than one commits’ effect to your index in a row.

  git revert -n 2b5c813cf2a3a5539f43f7ccfa38db7d9e3d572c

Git svn

Git svn Workflow

  # 99% of daily workflow
  git checkout -b <work_branch>
  #    ...hack...hack...
  git commit -a

  # switch back to master, then rebase against
  # any revisions in the svn repo
  git checkout master
  git svn rebase

  # now that master is current with svn,
  # sync working branch to local master
  git checkout <work_branch> # These two are the added steps
  git rebase master          # which help prevent conflicts

  # final upstream commit after rebasing
  git checkout master
  git svn rebase # one last check for new svn check ins
  git merge <work_branch>
  git svn dcommit -e

Git svn clone

  # You can clone (checkout) a specific revision from subversion by using the -r< option.  
  git svn clone -r1234 http://to.your.svn.repo

  # Clone a repo (like git clone):
  git svn clone http://svn.foo.org/project -T trunk -b branches -t tags

  # View all branches and tags you have cloned:
  git branch -r

  # Reset your master to trunk (or any other branch, replacing 'trunk'
  # with the appropriate name):
  git reset --hard remotes/trunk

  # You may only dcommit to one branch/tag/trunk at a time.  The usage
  # of dcommit/rebase/show-ignore should be the same as above.

Git log

git-log man page

git log is done in reverse date order (most recent first & medium detail).

Two most recent commits in reverse date order.

  git log -2 

Sample Output:

 
  commit 371a046dd57633c5a79ec864183b509b0f8219f3
  Author: jj <jj@b3081050-6727-0410-9636-b87e822ce5bb>
  Date:   Thu May 22 18:21:39 2008 +0000

      some message
      
      git-svn-id: https://host/trunk@13478 b3081050-6727-0410-9636-b87e822ce5bb

  commit 8784960fe821bb8153a365d62d449a5cc2107ff7
  Author: dd <dd@b3081050-6727-0410-9636-b87e822ce5bb>
  Date:   Thu May 22 05:55:37 2008 +0000

      fixing tests  
      
      git-svn-id: https://host/trunk@13472 b3081050-6727-0410-9636-b87e822ce5bb
 

Get detail stats about each commit ( message + how much each file has changed )

  git log --stat

Sample Output:


  commit 2918a904da9343a605e57bf3db88ed62a85ca41f
  Author: dd <dd@b3081050-6727-0410-9636-b87e822ce5bb>
  Date:   Wed May 21 22:24:36 2008 +0000

      excluding imports
  
      git-svn-id: https://host@13447 b3081050-6727-0410-9636-b87e822ce5bb

   app/models/foobar.rb |    1 +
   app/models/light.rb  |    4 +++-
   2 files changed, 4 insertions(+), 1 deletions(-)


  commit b32191937bffabeece147e13ceb5cc2f11700d29
  Author: dd <dd@b3081050-6727-0410-9636-b87e822ce5bb>
  Date:   Wed May 21 21:57:10 2008 +0000

      adding detail view to table.
    
      git-svn-id: https://host@13444 b3081050-6727-0410-9636-b87e822ce5bb

   app/views/admin/games/_download_csv.rhtml |    3 ++-
   app/views/admin/games/_play.html.erb      |   23 +++++++++++++----------
   app/views/admin/games/index.html.erb      |   24 ++++++++++++++----------
   3 files changed, 29 insertions(+), 21 deletions(-)

Remote Branches

Adding a Remote Repo

  mkdir repo.git
  cd repo.git
  git init
  touch .gitignore
  git add .
  git commit -m "added .gitignore" 
  # Down to here creates the local git repository and does first commit

  git remote add origin ssh://user@example.com/home/user/git/repo.git
  git config branch.master.remote origin
  git config branch.master.merge refs/heads/master
  # Configure the git repository

  git push --all
  # push the changes to the remote git repository.

Adding Local Remote Branch

  git remote add repo2 /path/to/local_repos2
  git remote show repo2
  git pull repo2 repo2_branch_name

Force Push admended Changes to Remote Repo

Stackoverflow Reference

  git push origin +master:master  

In general we can follow this push convention git push origin [local_repo]:[remote_repot]. The + sign will tell git not to enforce fast-forward only commit.