git-command

Git

After clone a repo from git server, git actually setups three areas for you, one is working directory(you alway see), staging(index) area(cache) and repository, git commands mainly work on these three areas, staging area is an intermediate area where commits can be formatted and reviewed before completing the commit.

One thing that sets Git apart from other tools is that it’s possible to quickly stage some of your files and commit them without committing all of the other modified files in your working directory or having to list them on the command line during the commit.

stage area

1
2
3
4
5
# you edit a.c b.c d.c while you only staged a.c b.c
$ git add a.c b.c

# only commit a.c b.c to repository
$ git commit -m "commit staged a.c b.c"

Add a ‘-a’ to your commit command in order to add all changed files to the staging area.

commit all changed

1
2
#  commit add modified a.c b.c d.c
$ git commit -am "commit a.c b.c d.c"

File Status

file status

Files in the working directory can be in the following states as shown by git status:

  • “Untracked”: the file in the working directory is not in the index. It must be added to the index before it can be committed to the repository.

  • “Changed but not updated”: The file in the working directory is not the same version as in the index. git add is used to add the working directory version to the index. If a commit is done before git add, the version in the index will be put in the repository. The version in the working directory be untouched and will be different than the version in the index and the repository.

  • “Changes to be committed”: The file in the working directory is the same version as in the index. The file is ready for a commit.

  • not listed: if a file is not shown in git status then the file in the working directory is the same version as in the index and repository.

  • “new file”: git add has been done (the file is in the index). The file has not been committed to the repository. It can be removed from the index or could be committed to the repository.

  • “modified”: the version in the working directory is different than the version in the index. It can be removed from the index or could be committed to the repository.

  • “deleted”: git rm has removed the file from the working directory and index but has not removed it from the repository. git reset HEAD file_name can be used to restore the file from the repository to the index and working directory. git commit will make the removal permanent.

config

In order to use git, it’s better to config it, here is an example of the config file located at ~/.gitconfig, edit it directly or use cli to do the config

1
2
3
4
$ git config --global user.name "Your Name"
$ git config --global user.email "your_email@whatever.com"
$ git config --global core.autocrlf input
$ git config --global core.safecrlf true

edit file directly

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
[push]
default = simple
[user]
name = Jason
email = xx@yy.com
[core]
#提交时转换为LF,检出时转换为CRLF
#git config --global core.autocrlf true
#提交时转换为LF,检出时不转换
#git config --global core.autocrlf input
#提交检出均不转换
#git config --global core.autocrlf false

#拒绝提交包含混合换行符的文件
#git config --global core.safecrlf true
#允许提交包含混合换行符的文件
#git config --global core.safecrlf false
#提交包含混合换行符的文件时给出警告
#git config --global core.safecrlf warn

autocrlf = input
safecrlf = warn
editor = vim
[pack]
windowMemory = 1g
[http]
postbuffer = 524288000
# sslverify = true
# proxy = http://proxy.company.com:3128
#[sendemail]
# smtpencryption = ssl
# smtpserver = stbeehive.xxx.com
# smtpuser = xx@yy.com
# smtpserverpot = 465
# suppresscc = all
# confirm = always

#you can suppress particular type emails to auto cc list by
#signedoffbycc = no
#suppressfrom = yes
#cocover = no
#cccover = no

#another alternate way, simple
#suppresscc=all
1
2
# check git config
$ git config --list

commands

git review

1
2
3
4
5
6
7
8
9
10
11
12
# this will check the .gitreview and push diff to that server
$ git review
$ cat .gitreview
[gerrit]
host=git.xxy.com
project=fw_ver/cdb
port=12023
track=true
usepushurl=true

# OR use below if review server and repo uses are same(ugly way)
# git push origin HEAD:refs/for/<branch_name>

git branch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# show local branches
$ git branch

# switch to branch
$ git checkout $branch

# create a branch based on current | tag | based branch
$ git branch $branch [tag|base branch]

# create then switch to new branch
$ git checkout -b $branch [tag | branchname]

# create a master(same name as remote branch) branch and set it track
$ git checkout --track origin/master

$ git branch -d $branch
$ git branch -D $branch

# as you know git pull pulls changes from remote server to remote branch
# origin/$branch, lets say if you create a branch from origin/master called master, if master does not track origin/master, so after git pull origin/master is updated but master is not, better set track for master branch

# show local and its remote tracking branch if has
$ git branch -vv
# set remote tracking branch(origin/master) for local master branch
$ git branch --set-upstream-to=origin/master master

# to see which local branch contains a particular commit
$ git branch --contains da6f215e4a6f379d094acd31c71c7696dae50afc

# to see which remote branch contains a particular commit.
$ git branch -r --contains da6f215e4a6f379d094acd31c71c7696dae50afc

# show remote branches
$ git branch -r

# push or create a new branch on remote server
$ git push origin $branch

# delete a branch from remote server
$ git push origin :$branch

# list branches which contains a given branch, also meaning the given branch merged into them
$ git branch --contains $branch -r

# list branches which are merged into a given branch, also meaning the given branch contains them
$ git branch --merged master -a

# list branches which are not merged into a given branch, also meaning the given branch does not contain them
$ git branch --no-merged master -a

diff/add/rm/commit/reset

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# compare working dir with staging area and staging area with repo then show status for all files
$ git status .
$ git status

# show modification of head commit or a given commit
$ git show
$ git show $commit
# show file content of a given commit
$ git show $commit:path/to/file

# show diff between two commits
$ git diff --name-only $commitidA..$commitidB

# show diff between working dir and staging area
$ git diff $file
$ git diff .
$ git diff

# show diff between staging area and repo
$ git diff --cached
$ git diff --cached $file

# add changes to staging area
$ git add .
$ git add $file
$ git add $dir

# commit changes
$ git commit -m "only commit changes in staging area"
$ git commit -a -m "no need git add, but only commit all tracked files"

# edit a submitted commit without creating a new commit
$ git add $update
$ git commit --amend

# reset staging area, modification in working dir remains
$ git reset --soft HEAD^ # reset to previous commit and keep modification
$ git reset --soft $commit

# reset staging area and working dir, repo both three part
# commit and changes before this commit are lost!!!
$ git reset --hard $commit

# discard changes in working dir
$ git checkout [$commit] -- $file
$ git checkout [$commit] -f $file

$ git checkout [$commit] -- $dir

# delete files from working dir
$ git rm *.c
$ git rm -r $dir

# delete files from index(staging area)
$ git rm --cached *.c

pull/push/merge/cherry-pick/rebase

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# fetch latest code from remote server to remote branch (git branch -r)
# update local branch(git branch) if they track remote branch.
$ git pull

# fetch latest code from remote server to remote branch (git branch -r)
# but not update local branch even they track remote branch
$ git fetch

# push commit changes on current branch to remote server(origin/xxx branch tracked)
$ git push [--force]

# push commit changes on another branch to remote server
$ git push origin $branch [--force]

# rebase can do: delete commit, edit commit, merge commit etc
# rebase the top 3 commits, but they may conflict when rebasing
$ git rebase -i HEAD~3
$ git rebase -i $commit # rebase from it to current

# if conflicts happen when rebasing do below or abort rebasing
$ git status . # check conflict files, the solve conflicts.
$ git add $fixed_conflict_file
$ git rebase --continue

# OR
$ git rebase --abort

# cherry-pick pick a commit or commits to local branch
# -x append a line that says "(cherry picked from commit …​)" to the original commit message
# -s --sign-off
# -e --edit
$ git cherry-pick -x -s -e $start^..$end [start-end]
$ git cherry-pick -x -s -e $start..$end (start-end]

$ git cherry-pick -x -s -e $commit
$ git cherry-pick -x -s -e $commit $commit # in order

# if conflicts happen when cherry-picking do below or abort
$ git status . # check conflict files
$ git add $fix_confict_files
$ git cherry-pick --continue

# OR
$ git cherry-pick --abort

# revert a commit, will create a new commit for this revert action
$ git revert -i $commit
$ git revert HEAD
$ git revert HEAD^ (revert two commits)

# merge dev to current branch, commit one by one at last create a merge commit
$ git merge dev
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
git pull vs git pull --rebase
when updating local branch(if it tracks remote branch), git pull will pull all new changes from remote branch then create a merge commit(contains all changes) on local branch
while git pull --rebase will cherry-pick new changes from remote branch to local branch

Example:
(D--E master is local branch)
Suppose you have two commits in local branch:

D---E master
/
A---B---C---F origin/master

After "git pull", will be:

D--------E
/ \
A---B---C---F----G master

After "git pull --rebase", there will be no merge point G. Note that (D and E become different commits!)

A---B---C---F---D'---E' master

git log

commit date vs author date
In Git, the author date is when someone first creates a commit with git commit.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# show logs
$ git log process.c # logs of one file
$ git log --oneline . # oneline logs of the current dir
$ git log -10 --reverse # top 10 logs of repo in reverse order
$ git log $old_commit..$new_commit # logs(commit) between two commits (old, new]
$ git log --oneline $old_commit~..$new_commit # logs(commit) between two commits [old, new]

# show logs of a particular author at a period of time
$ git log --author=jason --since="2016-02-01" # --before="2016-02-11"
$ git log -p $commit # show changes of given commit

####################### change author of commit ###################################
# change current commit author from command line
$ git commit --amend --author="Author Name <email@address.com>" --no-edit
# change Committer and Author for current commit
$ GIT_COMMITTER_NAME="Jason" GIT_COMMITTER_EMAIL="jaluo@163.com" git commit --amend --author="Jason <jaluo@163.com>" --no-edit

# without edit commit msg but update author based on currect ~/.gitconfig
$ git config user.name "New User"
$ git config user.email "newuser@gmail.com"
$ git commit --amend --reset-author --no-edit

####################### change date of commit ###################################
#for each commit, there are two dates, author date(when create the patch) and commit date
$ git log # only show author date, but the log is sorted by commit date)
$ git log --pretty=fuller # show both dates(author date and commit date)

# overwrite author date when edit a commit by --date option
# NOTE: this will reset commit date as well, equal with author date
# '+0800' is the timezone
$ git commit --amend --date=now
$ git commit --amend --date="Wed Jun 22 22:28:27 2022 +0800"

# without edit commit msg but update author date(also commit date) to now
$ git commit --amend --date=now --no-edit

# without edit commit msg but only update commit date to now
$ git commit --amend --no-edit

# show author date and commit data
$ git log --pretty=fuller .

# change commit date and author date to specific time
$ GIT_COMMITTER_DATE="Wed Jun 22 22:28:27 2022 +0800" git commit --amend --no-edit --date="Wed Jun 22 22:28:27 2022 +0800"

git patch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# create two patches HEAD~2 and print them out
$ git format-patch -2 --stdout
# create 2 patch files
$ git format-patch -2 -o out # save to out dir

# create one patch file with several commits
# commits with [cc1dde0dd, 6de6d4b06]
$ git format-patch cc1dde0dd^..6de6d4b06 --stdout > foo.patch
# each patch in this file is applied separately!!!
$ git am foo.patch

# apply a git patch
$ git apply [-p1] xx.patch

# apply with commit msg
$ git am xx.patch

# revert a applied patch
$ git apply --reverse xx.patch

# The --reject option will instruct git to not fail if it cannot determine how to apply a patch,
# but instead to apply individual hunks it can apply and create reject files (.rej) for hunks it cannot apply
git apply --reject --whitespace=fix xx.patch

git tag

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# show all tags
$ git tag -l

# create a tag on HEAD or given commit
$ git tag $tname
$ git tag $tname $commit

# update a tag with new commit
$ git tag -f $tname $new_commit
# delete a tag
$ git tag -d $tname

# push all local tags to remote server
$ git push --tags
$ git push -f --tags

# delete a tag from remote server
$ git push origin :refs/tags/$tname

# create tag with commit
$ git tag -a -m "comments" $tname [$commitid]

# update a tag
$ git tag -f -a -m "comments" $tname [$commitid]

# show the commit that a tag points to
$ git rev-list -1 $tname

# show all tags which contain a specific commit
$ git tag --contains 95d0a8e

merge commit from another repo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# nginx as another repo, want to merge one commint from nginx repo
$ git remote add nginx https://github.com/nginx/nginx.git
$ git remote show
nginx
origin

# show nginx remote branch
$ git remote show nginx
* remote nginx
Fetch URL: https://github.com/nginx/nginx.git
Push URL: https://github.com/nginx/nginx.git
HEAD branch: master
Remote branches:
branches/default new (next fetch will store in remotes/nginx)
branches/radix_with_skip new (next fetch will store in remotes/nginx)
branches/stable-0.5 new (next fetch will store in remotes/nginx)
branches/stable-0.6 new (next fetch will store in remotes/nginx)
branches/stable-0.7 new (next fetch will store in remotes/nginx)
branches/stable-0.8 new (next fetch will store in remotes/nginx)
branches/stable-1.0 new (next fetch will store in remotes/nginx)
branches/stable-1.10 new (next fetch will store in remotes/nginx)
branches/stable-1.12 new (next fetch will store in remotes/nginx)
branches/stable-1.14 new (next fetch will store in remotes/nginx)
branches/stable-1.16 new (next fetch will store in remotes/nginx)
branches/stable-1.18 new (next fetch will store in remotes/nginx)
branches/stable-1.2 new (next fetch will store in remotes/nginx)
branches/stable-1.20 new (next fetch will store in remotes/nginx)
branches/stable-1.4 new (next fetch will store in remotes/nginx)
branches/stable-1.6 new (next fetch will store in remotes/nginx)
branches/stable-1.8 new (next fetch will store in remotes/nginx)
master new (next fetch will store in remotes/nginx)
Local ref configured for 'git push':
master pushes to master (local out of date)

######################### Fetch ###########################
# fetch one branch to local than we can merge from it
$ git fetch nginx branches/stable-1.18
$ git branch -r
nginx/branches/stable-1.18

# Or fetch all branches to local
$ git remote update

######################### Merge ###########################
# Then cherry pick, or merge, delte remote is optional
... cherry pick, merge .....
$ git merge nginx/branches/stable-1.18 # merge to current branch
$ git remote remove nginx

# OR you can create a local copy or just use nginx/branches/stable-1.18
$ git checkout nginx/branches/stable-1.18 -b stable-1.18
... cherry pick, merge .....
$ git branch -D stable-1.18
$ git remote remove nginx

git filter-repo

Rewrite git history tool

1
2
3
4
$ pip3 install git-filter-repo

# examples
$

FAQ

cherry-pick part of a commit

1
2
3
4
$ git cherry-pick -x -s -e -n $commit  # -n is the key
$ git checkout HEAD -- $not_wanted_files
$ git add $wanted_file
$ git cherry-pick --continue

clean untracked files and untracked dirs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# Cleans the working tree by recursively removing files that are not under version control, starting from the current directory.
# Normally, only files unknown to Git are removed, but if the -x option is specified, ignored files are also removed. This can, for example, be useful to remove all build products.

# -d
# Remove untracked directories in addition to untracked files. If an untracked directory is managed by a different Git repository, it is not removed by default
# -x
# Don’t use the standard ignore rules read from .gitignore (per directory) and $GIT_DIR/info/exclude, but do still use the ignore rules given with -e
# options. This allows removing all untracked files, including build products. This can be used (possibly in conjunction with git reset) to create a pristine
# working directory to test a clean build.

# -f, --force
# If the Git configuration variable clean.requireForce is not set to false, git clean will refuse to run unless given -f or -n.
#
# -n, --dry-run
# Don’t actually remove anything, just show what would be done.

# -X
# Remove only files ignored by Git. This may be useful to rebuild everything from scratch, but keep manually created files.

# NOTE: -x will delete manually created files!!!
$ git clean -xdf

see commit for each line of a file

1
$ git annotate $file

check if local branch tracked or not

1
2
3
4
5
$ git branch -vv

# set track
$ git checkout master
$ git branch --set-upstream-to=origin/master

hide local change, not add to staging area

1
2
3
4
5
6
7
8
9
10
11
# you have to switch to another branch but you don't want to commit your local change and you want to keep the changes

$ git stash # hide the local change
$ git status # will show everything is up to date

$ git stash list # show the hidden change
$ git stash show -p stask@[2] # show the changed on that stash
$ git stash apply stash@[2] # restore that stash
$ git stash pop # restore the latest stashed local changes

$ git stash clear # drop all stashes, drop save hidden changes

move one repo from one server to another

1
2
3
4
5
6
# copy from org
$ git clone --bare $org_uri/project.git

# create a new repo at new server
$ cd project.git
$ git push --mirror $new_uri

find which commit introduces a bug

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# good as the low bad as the high commit()
# For example. one good commit you know is goodcmt
# one bad commit you know is badcmt
$ git bisect reset
$ git bisect start
$ git bisect good $goodcmt
$ git bisect bad $badcmt

#after this, the middle commit is checked out, test it

# if the middle commit works
$ git bisect good
# Or not work
$ git bisect bad

find which commit fixes the bug

1
2
3
4
5
6
7
8
9
10
11
12
13
# For example. one good commit you know is goodcmt
# one bad commit you know is badcmt
$ git bisect reset
$ git bisect start
$ git bisect good $badcmt
$ git bisect bad $goodcmt

#after this, the middle commit is checked out, test it

# if the middle commit works
$ git bisect bad
# Or not work
$ git bisect good

see history for git commit –amend

1
2
3
4
$ git reflog
# reset the latest git commit --amend and keep the change
$ git reset --soft @{1}
$ git reset HEAD

diff format

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
diff --git a/src/core/ngx_conf_file.c b/src/core/ngx_conf_file.c
index 98e9df6..359178c 100644
--- a/src/core/ngx_conf_file.c
+++ b/src/core/ngx_conf_file.c
@@ -1418,6 +1418,7 @@ ngx_conf_set_bitmask_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

np = (ngx_uint_t *) (p + cmd->offset);
value = cf->args->elts;
+ /* supported ignore haders */
mask = cmd->post;

for (i = 1; i < cf->args->nelts; i++) {
@@ -1429,11 +1430,13 @@ ngx_conf_set_bitmask_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
continue;
}

+ /* get supported ignore header */
if (*np & mask[m].mask) {
ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
"duplicate value \"%s\"", value[i].data);

} else {
+ /* set corresponding bit with 1, means ignore such header */
*np |= mask[m].mask;
}

Hunk 1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
-1418,6
-: old file
1418: old file line
6: continuous lines

+1418,7
+: new file
1418: new file line
7: continuous lines

6--->7 one line added

total means:
old file from 1418(total 6 lines) changed as below(diff part)
from line 1418(total 7 lines) in new file

Hunk 2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
-1429,11
-: old file
1429: old file line
11: continuous lines

+1430,13
+: new file
1430: new file line
13: continuous lines

11-->13 means two lines added

total means:
old file from 1429(total 11 lines) changed as below(diff part)
from line 1430(total 13 lines) in new file

# old line number counting no Hunk 1 applied
# new line counting with Hunk 1 applied

so you can see new line from 1430, as Hunk1 added 1 line!!!

how many commits

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# how many commits from HEAD to the beginning
$ git rev-list HEAD --count
$ git rev-list $commit --count

# find the nth commit from beginning
$git rev-list HEAD --count
total
$git log --skip=$(total-nth) --max-count=1

# find the 3th commit from beginning
$git rev-list HEAD --count
10
# 7 = 10-3
$git log --skip=7 --max-count=1

show commits of all users

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# with log title of each commit
$ git shortlog
Álvaro Fernández Rojas (1):
net: dsa: tag_brcm: add support for legacy tags

# without log title for each commit and no order
$ git shortlog -s
...
1 Álvaro Fernández Rojas

# order by author(commit count from high to low) for current dir
$ git shortlog -n -s .
66 Wang Xiao
52 lishaipeng
3 YaZenzeng
...

# order by author(commit count from high to low) for whole repo
$ git shortlog -ns --all --no-merges

# count commits submitted by given author
$ git shortlog -n -s --author=jason

see latest commit of all branches

1
2
3
4
# order latest commit of each branch by Date
$ git branch -r | grep -v HEAD | while read b; do git log -n 1 --color --format="%ci _%C(magenta)%cr %C(bold cyan)$b%Creset %s %C(bold blue)<%an>%Creset" $b ;done | sort -r
# get top 20
$ git branch -r | grep -v HEAD | while read b; do git log -n 1 --color --format="%ci _%C(magenta)%cr %C(bold cyan)$b%Creset %s %C(bold blue)<%an>%Creset" $b ;done | sort -r | head -20

submodule

submodule cheat-sheet

Ref