===
Git
===

    :Date: November 11, 2020

.. contents::



1 Learning Sources
------------------

- The following YouTube videos were helpful:

  - Introduction to Git - Core Concepts (by David Mahler): `https://youtu.be/uR6G2v_WsRA <https://youtu.be/uR6G2v_WsRA>`_

  - Introduction to Git - Branching and Merging (by David Mahler): `https://youtu.be/FyAAIHHClqI <https://youtu.be/FyAAIHHClqI>`_

  - Introduction to Git - Remotes (by David Mahler): `https://youtu.be/Gg4bLk8cGNo <https://youtu.be/Gg4bLk8cGNo>`_

  - Rewriting Git History - Amend, Reword, Delete, Reorder, Squash and Split (by "The Modern Coder"): `https://youtu.be/ElRzTuYln0M <https://youtu.be/ElRzTuYln0M>`_

- Web resources

  - Git reference manual: `https://git-scm.com/docs <https://git-scm.com/docs>`_

  - Git Handbook (by GitHub): `https://guides.github.com/introduction/git-handbook/ <https://guides.github.com/introduction/git-handbook/>`_

2 Basic concepts and terms:
---------------------------

- Overall structure: **working tree** <-(checkout)-|-(add)->  **staging area** <-(reset)-|-(commit)-> **history** <-(fetch/pull)-|-(push)-> **remote**

- difference: **working tree** <-> **staging area** <-> **history**

- working tree is also called the workspace

- staging area is also called as the index, cache, directory cache, current directory cache, staged files (`ref <https://gitguys.com/topics/whats-the-deal-with-the-git-index/>`_). It is a single, large, binary file in ``<baseOfRepo>/.git/index``, which LISTS all files in the current branch, their sha1 checksums, time stamps and the file name -- it is NOT another directory with a copy of files in it.

- history is also called as the local repository. This is a hidden directory (``.git``) including an ``objects`` directory CONTAINING ALL versions of every file in the repo (local branches and copies of remote branches) as a compressed `"blob" (Binary Large Object) <https://en.wikipedia.org/wiki/Binary_large_object>`_ file. (`ref <https://stackoverflow.com/questions/3689838/whats-the-difference-between-head-working-tree-and-index-in-git>`_)

- ``HEAD`` is the pointer to the working tree. To find where HEAD points to, run

  .. code:: shell

      cat .git/HEAD

- ``master`` is the pointer to the local master branch

- ``detached HEAD`` is the situation where ``HEAD`` (current working tree) is not ``master`` or with a branch name.

- ``WIP`` means Work In Progress (stashing)

- ``origin`` short name for the location of the remote

- ``origin/master`` is called the remote tracking branch (what the master branch looks like at ``origin``)

- ``upstream``: let A be a repository, B be a fork of A, C be a local clone of B. Then A is called the upstream of C

- ``fetch`` download remote to the local history

- ``push`` upload the local history to remote

- ``merge``

- ``tag`` specific points in a repository’s history as being important, eg. v1.0, v2.0, ...

- ``branch`` Local branches aren't automatically synchronized to the remotes so we have to explicitly push the branches you want to share. (TODO: continue to `Remote Branch <https://git-scm.com/book/en/v2/Git-Branching-Remote-Branches>`_)

3 .gitignore
------------

The file ``.gitignore`` lists files, per line, that should be ignored.

- comments starts with ``#``: ``# COMMENT``

- we can explicitly specify files, ``.DS_Store``

- we can use regexp, ``\*~``, ``\*.log``

- a whole folder is indicated by ``<FOLDER>/``

4 init
------

.. code:: shell

    git init

5 config
--------

.. code:: shell

    git config --global user.name "<NAME>"
    git config -- global user.email "<NAME@EMAIL.ADDRESS>"

.. code:: shell

    git config --local user.name "<NAME>"
    git config -- local user.email "<NAME@EMAIL.ADDRESS>"

To list config:

.. code:: shell

    git config --list

6 status
--------

.. code:: shell

    git status

7 log
-----

.. code:: shell

    git log

.. code:: shell

    git log -p

.. code:: shell

    git log -- <FILE>

.. code:: shell

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

It's convenient to alias this.

8 add: working tree -> staging area
-----------------------------------

.. code:: shell

    git add FILE

``FILE`` can be a list of files, ``FILE1 FILE2 FILE3 ...``
we can use regexp, for eg, ``S*`` for all files starting with ``S``

To add all modified files

.. code:: shell

    git add .

9 rm: remove from both working tree and the staging area
--------------------------------------------------------

.. code:: shell

    git rm <FILE>

10 commit: staging area -> history
----------------------------------

.. code:: shell

    git commit

To commit with a short commit message:

.. code:: shell

    git commit -m "<SHORT COMMIT MESSAGE>"

To commit without any commit message:

.. code:: shell

    git commit --no-edit

To amend the last commit:

.. code:: shell

    git commit --amend

(This does changes the ID of the last commit. Amending works only for the latest  commit.)

To commit without

11 add+commit: working tree -> staging area and history
-------------------------------------------------------

.. code:: shell

    git commit -a

nothing to specify after ``git rm``

12 diff: working tree <-> staging area
--------------------------------------

.. code:: shell

    git diff

13 diff: staging area <-> history
---------------------------------

.. code:: shell

    git diff --staged

14 checkout: working tree <- staging area
-----------------------------------------

.. code:: shell

    git checkout -- <FILE>

To place HEAD (i.e., to retrieve) to the state <HASH>:

.. code:: shell

    git checkout <HASH>

``<HASH>`` can be the first five characters

15 checkout: working tree and staging area <- history
-----------------------------------------------------

.. code:: shell

    git checkout <HASH> -- <FILE>

16 checkout branch
------------------

.. code:: shell

    git chekout <BRANCH NAME>

17 reset: staging area <- history
---------------------------------

.. code:: shell

    git reset HEAD <FILE>

18 branch: list
---------------

To list all branches:

.. code:: shell

    git branch

To list merged branches:

.. code:: shell

    git branch --merged

To list local and remote branches:

.. code:: shell

    git branch -a

To list remote tracking branches only:

.. code:: shell

    git branch -r

19 branch: create new
---------------------

To create a new branch from HEAD:

.. code:: shell

    git branch <BRANCH NAME>

To create + checkout -> new branch:

.. code:: shell

    git branch -b <BRANCH NAME>

20 branch: remove
-----------------

.. code:: shell

    git branch -d <BRANCH>

21 merge: HEAD -> master
------------------------

To merge <BRANCH> into master:

.. code:: shell

    git merge <BRANCH>

The response can be different depending on the strategy:

- fast-forward

- three-way (recursive)

  - In case of conflict, files with conflict get modified with diff contents. We can check and resolve these conflict by opening those files. When doing this, we also need to delete git markers.

  - After conflicts are resolved, do ``git status``.

  - Aborting the merge process will restore the original file contents:

  .. code:: shell

      git merge --abort

If all named commits are already ancestors of HEAD, git merge will exit early with the message "**Already up to date.**" This is the situation when we try to merge the same branch. In such a situation, differentiate two points by distinct branches, merge, and then remove the unnecessary branch name.

**After merging** with ``master`` branch with another branch, ``<BRANCH>`` to say, the branch ``<BRANCH>`` that may not be necessary anymore is not automatically deleted. We have to this manually:

.. code:: shell

    git branch -d <BRANCH>

22 restore
----------

.. code:: bash

    $ git checkout <branch> <filename>

will bring back ``<filename>`` from ``<branch>``. This can be used when we want to restore a deleted or modified file.

23 Stash: save
--------------

To save working directory without staging:

.. code:: shell

    git stash

To stash with comment:

.. code:: shell

    git stash save "<comment>"

24 stash: list
--------------

To list all stashes:

.. code:: shell

    git stash list

To list all stashes with changes:

.. code:: shell

    git stash list -p

25 stash: apply
---------------

.. code:: shell

    git stash apply

.. code:: shell

    git stash apply <LABEL>

Here, ``<LABEL>`` is the one given by ``stash list``

26 clone
--------

.. code:: shell

    git clone git@github.com:name/git.git

27 remote: add/remove
---------------------

To add a new remote:

.. code:: shell

    git remote add origin <REMOTE REPOSITORY URL>


To add upstream:

.. code:: shell

    git remote add upstream <UPSTREAM REPOSITORY URL>

To remove:

.. code:: shell

    git remote remove <REMOTE NAME>

After modifying remote/upstream, run ``git remote -v`` to verify.

28 remote: list
---------------

To list remotes (short names only):

.. code:: shell

    git remote

To list remotes (short and full names only):

.. code:: shell

    git remote

To list remotes verbosely:

.. code:: shell

    git remote -v

29 fetch: local repository <- remote repository
-----------------------------------------------

.. code:: shell

    git fetch origin

Note that this does not affect local/HEAD. We need to merge, eg. ``git merge origin/master`` later.

.. code:: shell

    git fetch upstream

To fetch and merge:

.. code:: shell

    git pull <REMOTE> <BRANCH>

The error "fatal: refusing to merge unrelated histories" may occur when we try to merge/pull unrelated repositories together. For example, we might try to resume old project from scratch while wanting to keep the same GitHub repository. To resolve this:

.. code:: shell

    git pull <REMOTE> <BRANCH> --allow-unrelated-histories

30 push: local repository -> remote repository
----------------------------------------------

.. code:: shell

    git push <REMOTE REPOSITORY: eg. origin> <LOCAL BRANCH: eg. master>

.. code:: shell

    $ git push --set-upstream origin master
    To https://...
     ! [rejected]        master -> master (fetch first)
    error: failed to push some refs to 'https://...'
    hint: Updates were rejected because the remote contains work that you do
    hint: not have locally. This is usually caused by another repository pushing
    hint: to the same ref. You may want to first integrate the remote changes
    hint: (e.g., 'git pull ...') before pushing again.
    hint: See the 'Note about fast-forwards' in 'git push --help' for details.
    hbshim@aleph:~/Dropbox/Workspace/math/build/html$ git push -f
    fatal: The current branch master has no upstream branch.
    To push the current branch and set the remote as upstream, use

        git push --set-upstream origin master

.. code:: bash

    $ git push -f

31 rebase
---------

To "operate on" the last <n> commits back from HEAD:

.. code:: shell

    git rebase -i HEAD~<n>

(``-i`` indicates "interactive".)

Interactive commands:

- ``reword``: edit commit message

- ``drop``: remove commit

We can also reorder commits by changing the order of the lines.

32 cherry-pick
--------------

33 tag
------

To list tags,

.. code:: shell

    git tag


To create an annotated tag named ``TAG``:

.. code:: shell

    git tag -a <TAG> -m "<COMMENT>"

To see the data for the tag named ``TAG``

.. code:: shell

    git show <TAG>

To push the tag ``TAG`` to the remote ``original``:

.. code:: shell

    git push origin <TAG>

To push all tags to the remote ``original``:

.. code:: shell

    git push origin --tags

To delete the tag ``TAG``:

.. code:: shell

    git tag -d <TAG>

Caution: This does not remove ``TAG`` from the remote.

To delete the tag ``TAG`` from the remote ``original``:

.. code:: shell

    git push origin --delete <TAG>

To checkout ``TAG``:

.. code:: shell

    git checkout <TAG>

This will cause the "detached HEAD" state since what was checked out is merely a commit hash not a branch (even if there was branch tip pointing to the commit ``TAG``). We need to branch out to work on and modify the commit hash ``TAG``:

.. code:: shell

    git checkout -b <BRANCH> <TAG>

git should respond with the message "Switched to a new branch ``<BRANCH>``."