git.rst
author Oleksandr Gavenko <gavenkoa@gmail.com>
Sat, 10 Feb 2018 01:36:16 +0200
changeset 2229 1a0b6597e594
parent 2214 f7b3d90697ab
child 2232 ac7f6d8bea28
permissions -rw-r--r--
Mark block quot in HTMLe.

.. -*- coding: utf-8; -*-

======
 Git.
======
.. contents::
   :local:

Setting up git
==============

Debian::

  $ sudo apt-get install git

Cygwin::

  $ setup.exe -p git
  $ apt-cyg install git

After install set up some options::

  $ git config --global user.name "Oleksandr Gavenko"
  $ git config --global user.mail "gavenkoa@gmail.com"

After that you should have::

  $ cat ~/.gitconfig
  [user]
  name = Oleksandr Gavenko
  mail = gavenkoa@gmail.com

git over proxy.
===============

Only http:// protocol support proxy (not git://)::

  $ export http_proxy="http://username:password@proxy:port/"
  $ git clone http://github.com/$user/$proj.git $proj

You can store proxy settings under repository local config file::

  $ git config http.proxy http://$user:$passwd@$ip:$port

Start project with git
======================

Setup proj space on fs::

  $ mkdir proj
  $ cd proj
  $ git init
  Initialized empty Git repository in /home/user/tmp/proj/.git/
  $ ls -a
  . .. .git

Add file, make changes, commit all::

  $ emacs Makefile
  ... C-x C-c
  $ emacs app.c
  ... C-x C-c
  $ git add Makefile app.c
  $ git status
  # On branch master
  #
  # Initial commit
  #
  # Changes to be committed:
  #   (use "git rm --cached <file>..." to unstage)
  #
  #       new file: Makefile
  #       new file: app.c
  #

or just::

  $ git add .

Commit newlly added file::

  $ git commit
  ... Write message log ...
  Created initial commit 2169263: My first commit massage.
   1 files changed, 4 insertions(+), 0 deletions(-)
   create mode 100644 app.c

Show difference
===============

Show difference between index/stage and working tree::

  $ git diff

Show difference between ``HEAD`` and index (what is going to be commited)::

  $ git diff --cached
  $ git diff --staged

.. note:: ``--cached`` and ``--staged`` are synonyms.

Show difference beetween ``HEAD`` and working tree (what is commited after ``git commit -a``)::

  $ git diff HEAD
  $ git diff HEAD -- $path

Show difference in changeset::

  $ git show $hash

Show difference between revisions::

  $ git diff $rev1..$rev2
  $ git diff ORIG_HEAD..HEAD

https://stackoverflow.com/questions/3293607/difference-between-git-head-and-the-current-project-state
  Difference between git HEAD and the current project state?

Undo tracking added file.
=========================

You do::

  $ git add badfile
  $ git status
  # On branch master
  # Changes to be committed:
  #   (use "git reset HEAD <file>..." to unstage)
  #
  #       new file:   badfile
  #

To stop tracking badfile do::

  $ git rm --cached badfile
  $ git status
  # On branch master
  # Untracked files:
  #   (use "git add <file>..." to include in what will be committed)
  #
  #       file
  nothing added to commit but untracked files present (use "git add" to track)

or::

  $ git reset badfile

Committing changes
==================

Individual file::

  $ $EDITOR Makefile
  $ git add Makefile
  $ git diff --cached
  $ git commit -m "..."

Commit all changed and previously manages files::

  $ git commit -a -m "...."

Managing branches
=================

Print current branch::

  $ git branch

List all known branches::

  $ git branch -a

List all known remote branches::

  $ git branch -r

List all remote branches (from all remotes)::

  $ git ls-remote
  $ git ls-remote origin

List registered remotes::

  $ git remote show

List remote branches from ``$REMOTE`` remote::

  $ git ls-remote --heads $REMOTE
  $ git remote show $REMOTE

Special case of above is ``origin`` remote, which is default remote::

  $ git remote show origin
  $ git ls-remote --heads origin

.. note::

   Look to ``[remote "..."]`` in ``~/.git/config`` to find out names of possible
   remotes. Alternatively get list from::

     $ git remote show

Getting branches pointers from default (``origin``) remote (without merging
tracking branch)::

  $ git fetch

Getting branches pointers from ``$REMOTE`` remote::

  $ git fetch $REMOTE

To get updated with default remote changes::

  $ git pull

or specify concrete remote::

  $ git pull $REMOTE
  $ git pull origin

Delete local branch::

  $ git branch -d $NAME

Delete remote branch remotely::

  $ git push --delete $REMOTE $BRANCH_NAME
  $ git push -d $REMOTE $BRANCH_NAME
  $ git push -d origin $BRANCH_NAME

To locally remove pointers to deleted remotely branches::

  $ git remote update --prune
  $ git remote update --prune origin

.. note::

   Only curtain branches fetched by default::

     [remote "origin"]
     url = ...
     fetch = +refs/heads/*:refs/remotes/origin/*

   Edit ``fetch`` value to change defaults.

Move branch pointer to arbitrary hash::

  $ git branch -f $NAME $HASH

git analog of 'hg incoming'
===========================

git does not directly support such feature. You can emulate it by::

  $ git fetch
  $ git log master..origin/master   # or just '..origin/master'

By previous commands you grab changes from remote server! You can apply them by
``pull`` or ``merge`` or ``rebase`` command::

  $ git pull
  $ git merge
  TODO

To detect if remote repository have new changes::

  $ git ls-remote
  $ git ls-remote $upstream

View git commits
================

Review specific commit with::

  $ git show $REV
  $ git diff $REV^ $REV

Review difference between commits::

  $ git $REV1 $REV2
  $ git $REV1..$REV2
  $ git $REV1..$REV2 -- $DIR_OR_FILE

Review history with diffs::

  $ git log -p
  $ git log -p $FROM_REV

View unpublished git commits / analog of git 'hg outgoing'
==========================================================

``git`` does not directly support such feature. Recently ``hg`` start tracking
changes that pushed to any other repositories (called as ``public`` versus
``draft`` which is not yet published anywhere). You can emulate it by::

  $ git fetch origin
  $ git log origin/master..master
  $ git log origin/master..
  $ git log origin/master..HEAD
  $ git log @{u}..

  $ git diff origin/master..

Verbose syntax::

  $ git log --branches --not --remotes

http://stackoverflow.com/questions/2016901/viewing-unpushed-git-commits
  Viewing unpushed Git commits.
http://stackoverflow.com/questions/3636914/how-can-i-see-what-i-am-about-to-push-with-git
  How can I see what I am about to push with git?

git analog of 'hg glog'
=======================
::

  $ git log --all --graph
  $ git log --all --graph --oneline
  $ git log --all --graph --oneline --decorate

Add alias::

  [alias]
  glog = log --all --graph

Git analog of 'hg rollback'
===========================

To edit commit message of last commit::

  $ git commit --amend -m "$MSG"

To integrate changes into last commit (``-a`` to avoid antecedent ``git add ...``,
``--no-edit`` if you don't like to change commit message, otherwise external
editor is opened)::

  $ git commit -a --amend --no-edit

To edit messages of old commits starting from ``$REV``::

  $ git rebase -i $REV

To undo latest commit::

  $ git reset HEAD~1

Git analog of 'hg root'
=======================
::

  $ git rev-parse --show-toplevel

Making local branch tracking remote
===================================
::

  $ git branch -u upstream/foo
  $ git branch -u upstream/foo foo

  $ git branch --set-upstream-to=upstream/foo
  $ git branch --set-upstream-to=upstream/foo foo

Showing what and how local branches are tracked::

  $ git branch -vv
  $ cat .git/gitconfig

Showing what remote branches tracked::

  $ git remote show $remote

Undo mistaken rebase
====================

If there are no any ``reset``, ``rebase`` or ``merge`` afterwards the easiest
way to recode to pre-rebase state is one of::

  $ git rebase --abort
  $ git reset --hard ORIG_HEAD

Otherwise look to::

  $ git reflog

and reset to necessary head::

  $ git reset --hard "HEAD@{...}"

https://stackoverflow.com/questions/134882/undoing-a-git-rebase
  Undoing git rebase.

Undo reset --hard
=================

``git reset --hard`` is destructive command without backup data.

You may find your previously added data in Git garbage::

  $ git fsck --lost-found

look to files inside ``.git/lost-found`` directory.

Reviewing ``git reflog`` also may help.

* https://stackoverflow.com/questions/5788037/recover-from-git-reset-hard
* https://stackoverflow.com/questions/14251194/how-to-recover-after-i-execute-git-reset-hard-head

Git bisect
==========

``bad`` changes should be later in graph history then ``good`` ones. To use
alternative names::

  $ git start --term-old=... --term-new=...

Start bisecting with::

  $ git bisect start

Mark good and bad revisions::

  $ git co vBAD
  $ make test
  $ git bisect bad

  $ git co vGOOD
  $ make test
  $ git bisect good


If build/test failed to complete use::

  $ git bisect skip

To restore mistakenly marked revisions::

  $ git bisect log >$LOG
  $ git bisect reset
  $ $EDITOR $LOG
  $ git bisect replay $LOG

Import patch
============

Check patch summary::

  git apply --stat my.patch

Detect possible errors during patch application::

  git apply --check my.patch

Apply patch to working tree without commit::

  git apply my.patch

Commit patch::

  git am my.patch

Commit patch by signing you as reviewer::

  git am --signoff my.patch

Debug git network operation
===========================

Git uses libcurl for network operation::

  $ export GIT_CURL_VERBOSE=1
  $ git ...

Push new repo to remote.
========================
::

  $ mkdir $REPO
  $ cd $REPO
  $ git init
  $ git add .
  $ git commit -m "Initial commit"
  $ git remote add origin https://$USER:$PASS@$HOST/$REPO
  $ git push -u origin master

Show heads in branch.
=====================
::

  $ git show-ref --heads -s

Search string in file
=====================

To search string in all or specific files or working copy::

  $ git grep $PATT
  $ git grep -i $PATT -- '*.[ch]'

To search in all history::

  $ git log -S$PATT
  $ git log --pickaxe-regex=$PATT
  $ git log -G$PATT
  $ git grep -i PATT $(git rev-list --all) -- '*.[ch]'

.. note::

   ``-S`` search occurences in diff chunks, while ``-G`` detects changes in match count.

https://git-scm.com/book/en/v2/Git-Tools-Searching
  Git Tools - Searching.
https://stackoverflow.com/questions/1337320/how-to-grep-git-commit-diffs-or-contents-for-a-certain-word
  How to grep Git commit diffs or contents for a certain word?
https://stackoverflow.com/questions/4468361/search-all-of-git-history-for-a-string
  Search all of Git history for a string?
https://stackoverflow.com/questions/2928584/how-to-grep-search-committed-code-in-the-git-history
  How to grep (search) committed code in the git history?

Find most recent tag for revision.
==================================
::

  $ git describe $REV

Many projects set tags on branches rather then mainline (for example JS/CSS
projects perform build and commit binary/minified files, which unnecessary for
mainline history). In this case review simplifiied history by::

  $ git log --graph --all --decorate --oneline --simplify-by-decoration

List tags with dates.
=====================
::

  $ git log --tags --simplify-by-decoration --pretty="format:%ci %d"

Update to date.
===============
::

  $ git checkout 'master@{1979-02-26}'
  $ git checkout 'master@{1979-02-26 18:30:00}'

Using git to work with SVN offline.
===================================

Prepare SVN and git utilities::

  $ sudo apt-get svn git-core git-svn

Making SVN repo::

    $ cd $HOME/tmp
    $ svnadmin create svn-repo
    $ svn co file://$HOME/tmp/svn-repo svn-devel
    $ cd svn-devel
    $ mkdir tags trunk branches
    $ svn add *
  A         branches
  A         tags
  A         trunk
    $ cd trunk/
    $ printf "all:\n  echo XXX\n" >Makefile
    $ printf "int main() {return 0;}" >main.c
    $ svn add *
    $ cd ..
    $ svn st
  A      trunk
  A      trunk/main.c
  A      trunk/Makefile
  A      branches
  A      tags
    $ svn ci -m init
  Adding         branches
  Adding         tags
  Adding         trunk
  Adding         trunk/Makefile
  Adding         trunk/main.c
  Transmitting file data ..
    $ svn cp -m v0.0 file://$HOME/tmp/svn/trunk file://$HOME/tmp/svn/tags/v0.0
    $ svn cp -m v0.1 file://$HOME/tmp/svn/trunk file://$HOME/tmp/svn/branches/v0.1

Moving SVN changset to git repo::

    $ cd $HOME/tmp
    $ git svn init file://$HOME/tmp/svn git-repo
    $ ls -a
  .  ..  .git
    $ git svn fetch
      A   trunk/main.c
      A   trunk/Makefile
  W: +empty_dir: branches
  W: +empty_dir: tags
  r1 = 52ccd9882979dd53ec198dbac108783ec660410f (git-svn)
      A   tags/v0.0/main.c
      A   tags/v0.0/Makefile
  r2 = 8ec8a772bb6f37ace56b3649066dc84e481ed427 (git-svn)
      M   trunk/Makefile
  r3 = 2c169ff409ed504dd6a092b1e302beb3fd94871e (git-svn)
      A   branches/v0.1/main.c
      A   branches/v0.1/Makefile
  r4 = e68d76f4ba6beea4b9059c1884c1f38ce10831a7 (git-svn)
      M   trunk/Makefile
  r5 = cdde63974454b13ac53f2eeb201aa76c49fd875c (git-svn)
  Checked out HEAD:
    file:///home/sasha/tmp/svn r5

or (in old git version)::

  $ git svn clone file://$HOME/tmp/svn git-repo

Making changes in svn::

    $ cd $HOME/tmp/svn-devel/trunk
    $ echo ".PHONY: clean" >>Makefile
    $ svn ci -m "Added clean to phony."
  Sending        trunk/Makefile
  Transmitting file data .
  Committed revision 6.

Making committed in git::

    $ cd $HOME/tmp/git-repo/trunk
    $ echo ".PHONY: all" >>Makefile
    $ echo "int foo(int x) {return x+x;}" >>main.c
    $ git status
  # On branch master
  # Changed but not updated:
  #   (use "git add <file>..." to update what will be committed)
  #
  #       modified:   Makefile
  #       modified:   main.c
  #
  no changes added to commit (use "git add" and/or "git commit -a")
    $ git commit -a -m "Bug fixed."
  Created commit 222d399: Bug fixed.
   2 files changed, 2 insertions(+), 0 deletions(-)

Getting changes from SVN to git::

    $ git svn rebase
      M   trunk/Makefile
  r6 = 8165e9bfb38e9df09a7313d19606ec227629b670 (git-svn)
  First, rewinding head to replay your work on top of it...
  Applying Bug fixed.
  error: patch failed: trunk/Makefile:6
  error: trunk/Makefile: patch does not apply
  Using index info to reconstruct a base tree...
  Falling back to patching base and 3-way merge...
  Auto-merged trunk/Makefile
  CONFLICT (content): Merge conflict in trunk/Makefile
  Failed to merge in the changes.
  Patch failed at 0001.

  When you have resolved this problem run "git rebase --continue".
  If you would prefer to skip this patch, instead run "git rebase --skip".
  To restore the original branch and stop rebasing run "git rebase --abort".

  rebase refs/remotes/git-svn: command returned error: 1
    $ git add Makefile
    $ git rebase --continue
  Applying Bug fixed.

and return all from git to SVN::

    $ git svn dcommit
  Committing to file:///home/sasha/tmp/svn ...
      M   trunk/Makefile
      M   trunk/main.c
  Committed r7
      M   trunk/main.c
      M   trunk/Makefile
  r7 = 68e782c8d06635f2db4dd69b9ca8599f99da22e2 (git-svn)
  No changes between current HEAD and refs/remotes/git-svn
  Resetting to the latest refs/remotes/git-svn

See what going to SVN repo::

    $ cd $HOME/tmp/svn-devel/trunk
    $ svn diff -r BASE:HEAD
  Index: Makefile
  ===================================================================
  --- Makefile    (working copy)
  +++ Makefile    (revision 7)
  @@ -6,4 +6,4 @@
   .o: .c
      $(CC) $(CFLAGS) -c -o $@ $<

  -.PHONY: clean
  +.PHONY: all clean
  Index: main.c
  ===================================================================
  --- main.c  (working copy)
  +++ main.c  (revision 7)
  @@ -2,3 +2,4 @@
   {
       return 0;
   }
  +int foo(int x) {return x+x;}
    $ svn up
  U    Makefile
  U    main.c
  Updated to revision 7.

gitk.
=====

gitk - The git repository browser. See gitk(1).

Installing::

  $ sudo apt-get instal gitk

Using::

  $ cd /path/to/git-repo
  $ gitk