Git Usage

Table of Contents

Git is an awesome tool for version control. I used to some convenient GUI applications to manage git repository, and ignored how it works and how to do it by command line. So in this article I will record some useful git commands that I used in development.

Initialization

Initialize a local git repository

git init
# recommand the way using ssh
git remote add origin ssh@xxxx

Push to remote repository

git add .
git commit -a
git push origin master

Pull from remote repository

git pull origin master

List the available remote repositories

git remote -v

Branch

Create

# create a branch named "branch1"
git checkout -b branch1

Checkout (from local)

git checkout master

Checkout (from remote to local)

git checkout -b branch1 origin/branch1

Merge

# assume you are in master branch and want to merge the code from hotfix
git merge hotfix

Rename

# rename
git branch -m old-name new-name
# Delete the old-name remote branch and push the new-name local branch.
git push origin :old-name new-name
# Reset the upstream branch for the new-name local branch.
git push origin -u new-name

Delete local Branch

git branch -d branch_name
# force delete
git branch -D branch_name

Delete remote branch (remote name: origin, remote branch: branch1)

git push origin --delete branch1

Tag

Create

# add tag(e.g. v1.0) for current branch
git tag v1.0
# add tag with commit id(e.g. 039bf8b)
git tag v1.0 039bf8b
# add tag with extra message
git tag -a v1.0 -m "version 1.0"

View

# view local tags
git tag
# view remote tags
git ls-remote --tags

Share the Tags

# push a specific tag
git push origin v1.0
# push all local tags for one time
git push origin --tags

Switch

git checkout v1.0

Delete remote tag(e.g. v1.0)

git push -d origin v1.0

Submodule

Init all submodules.

git submodule update --recursive --init

Update all submodules.

git submodule update --recursive --remote

Reset all submodules.

git submodule foreach git reset --hard

Pull

Pull forcely.

git reset --hard origin/master
git pull

Fix untracked files

For example, although you added some files into .gitignore, those are still tracked by git. The solution of the problem shown in below:

git rm -r --cached .
git add .
git commit -m "fixed untracked files"

How to undo the most recent commits

The more details could be found in StackoverFlow.

# your files will not lose, even index not too
# you can simply recommit again
git reset --soft HEAD~
git commit -a -m "recommit"
# reset with "--hard" would switchs the previous state entirely
# that means this not leaves your files and index both
git reset --hear HEAD~

Resolve a merge conflict using the command line

The more details could be found in Github Documents.

git status
# >>> OUTPUTS OF git status <<<
# On branch master
# You have unmerged paths.
# (fix conflicts and run "git commit")
# (use "git merge --abort" to abort the merge)
#
# Changes to be committed:
#
# new file: socket_learning/class3/__init__.py
# new file: socket_learning/class3/db/Message.txt
# new file: socket_learning/class3/db/User.txt
# new file: socket_learning/class3/models/message.py
# new file: socket_learning/class3/models/user.py
#
# Unmerged paths:
# (use "git add/rm <file>..." as appropriate to mark resolution)
#
# deleted by us: socket_learning/class3/models/__init__.py
# added by us: socket_learning/server_impl/models/__init__.py
# both modified: socket_learning/server_impl/routes.py
#
# Untracked files:
# (use "git add <file>..." to include in what will be committed)
#
# socket_learning/__pycache__/
# socket_learning/server_impl/__pycache__/

delete by us and added by us can be added or deleted simply using the following command.

git add <file>
git rm <file>

both modified should manually merge using the text editor, like Vim or Atom. In these case, the conflict of routes.py is showing as below

<<<<<<< HEAD:socket_learning/server_impl/routes.py
from socket_learning.server_impl.models.message import Message
from socket_learning.server_impl.models.user import User
from socket_learning.server_impl.utils import log
=======
from socket_learning.class3.models.message import Message
from socket_learning.class3.models.user import User
from socket_learning.class3.utils import log
>>>>>>> d8a8b9b8ad0e55b7216520600ae1bf809b5b2aa7:socket_learning/class3/routes.py

<<<<<<<, ======= and >>>>>>> indicates the places where conflict occurred, you should determine what the contents is the right one and change it with text editor.

# handle the conflicts manually
from socket_learning.server_impl.models.message import Message
from socket_learning.server_impl.models.user import User
from socket_learning.server_impl.utils import log

Save it and recommit them.

git add .
git commit -a -m "Merge branch 'master' of github.com:xavier-niu/python-playground"

Remove all history commits

Checkout

# the core use for "git checkout --orphan" is to reach
# a "git init"-like situation on a non-new repository
git checkout --orphan latest_branch

Add all the files

git add -A

Commit the changes

git commit -am "commit message"

Delete the master branch (or what branch you want to clean)

git branch -D master

Rename the current branch to master

git branch -m master

Finally, force update your repository

git push -f origin master

Integrate commit and push into one command

This not supports by the build-in git command. But we can add a customized function into .bash_profile(for mac) to implement it.

# lazygit for using commit and push in one command
# usage: lazygit "commit msg"
function lazygit() {
git add .
git commit -am "$1"
git push
}

Restore the deleted file

Assume that test.txt is the file you want to restore. First of all, find the last commit that affected the given path.

git rev-list -n 1 HEAD -- test.txt
# outputs: 145107deebdbbad4ce0824b0dbb6f648b5515de7

The outputs are the id of the last commit. Checkout the version at the commit before, using the caret symbol.

git checkout 145107deebdbbad4ce0824b0dbb6f648b5515de7^ -- text.txt

Or in one command

git checkout $(get rev-list -n 1 HEAD -- text.txt)^ -- text.txt

Refuse to Merge Unrelated Histories

Git reports an error when pulling from remote source.

XavierNius-iMac:lucius-dev xavierniu$ git pull origin master
From github.com:xavier-niu/docker-lucius
 * branch            master     -> FETCH_HEAD
fatal: refusing to merge unrelated histories

The following command with --allow-unrelated-histories is work.

git pull origin master --allow-unrelated-histories

Git Clone with Specific Port

The git clone command with default port is

git clone git@localhost:root/lucius.git

Assume that the port is 1923, the command is switch to

git clone ssh://git@@localhost:1923/root/lucius.git

What we added:

  • ssh protocol at the beginning of address
  • the port you specified behind the colon

Move Files When Checkout a New Branch without Commit or Merge

Sometimes, I often forget to checkout a new branch when composing a new module. Therefore, I don't want to commit it, neither want to merge it, but only would like to save current status and change the branch in that I haven't reached the milestone. To achieve it, I recommend to use stash command.

# step 1
git stash
# step 2: checkout
git checkout new-branch
# step 3: restore current status
git stash pop

Use Git over a Proxy

Assume that there is a proxy like 127.0.0.1:1080, both http and ssh proxy should be configed to ensure we can access the remote resources through any ways, like ssh protocol.

# git http proxy
git config --global http.proxy socks5://127.0.0.1:1080

Add the following lines into ~/.ssh/config, this method is passed on the macOS, other OS I haven't test.

Host github.com
    ProxyCommand          nc -X connect -x 127.0.0.1:1081 %h %p
    ServerAliveInterval   10m

BTW, in the git http proxy config, you should use socks proxy with the "socks5://" prefix, on the contrary, in the ssh config, you should use http proxy.


Update Logs

This post will be updated continuously with my development. There are the logs to better know when I updated those contents:

  • January 20, 2019: init move all contents from Notion to my blog
  • January 21, 2019: add remove all history commits
  • January 22, 2019:
    • add branch > delete remote branch
    • update tag > create
    • add tag > delete remote tag
    • update tag > share the tags
    • update tag > view
  • January 27, 2019: add integrate commit and push into one command
  • February 3, 2019: add restore the deleted file
  • February 27, 2019: add refuse to merge unrelated histories
  • March 8, 2019: add git clone with specific port
  • March 17, 2019: add move files ehen checkout a new branch without commit or merge
  • April 4, 2019: add use git over a proxy