Git 高级用法 [原创]

先复习一下git的一般命令。。

git add -A

git commit -m “update” // git commit -am “update” # the second means to add modifications to all the existed files added through git add -A, but newly created files are not added.

git push 

git submodule update –init –recursive

// at local working branch, generate patch. generated patches are differences from each commit to the compared-commit.

git format-patch -M compare-commit

// at remote branch, apply the patch(add -3 if need to manual merge):

git am -3 0000-commit-info.patch

 

 **一些GIT环境变量GIT_CURL_VERBOSE=1; GIT_TRACE_PACKET=2; GIT_NO_SSL_VERIFY=1**

fatal because ssl cert not installed.. :

workaround:

git config --global http.sslVerify false

solve: by installing mozilla-rootcerts

git config --system http.sslCAPath /path/to/cacerts

 

git存在几个重要概念。。工作目录(working directory),快照(snapshot),commit历史(commit history)以及暂存区(staging area)。

工作目录就是包含.git文件夹的父目录,快照就是该目录下当前的工程文件,commit历史可以通过git log来形象地理解,暂存区就是提交为待commit的改动。

一般修改工程时是在修改工作目录,修改完毕后add就将改动放到了暂存区,commit后暂存区的改动就被提交到了commit历史中,同时对应的也有了这个commit的快照。push把当前commit上传。

git有分支的概念,个人认为一个模块一个branch最好,到时候merge可以一个个来。但是人多的话还是按人分。

HEAD表示当前commit history所指向的commit。

git pull = git fetch master:temp; git merge temp;(如果看不懂,先看下面的refspec)

 

以下内容图片来源

1. git reset(在某个之前/最新的自己的commit分支开始修改时用), git revert(修改public分支用), git checkout(查看过去某个commit时用)

reset有三个选项–soft,–mixed(default),–hard,要理解这三个首先要理解本地的git存在三条线:工作目录(working directory),快照(snapshot)以及commit历史。soft就是只更改commit历史,mixed是更改快照以及commit历史,hard是三者同时更改。要注意的就是如果reset了工作文件(hard),那么再想reset回去到之前的HEAD是不行的了。这个一般没法用于public branch。(想一想为什么?)而且reset命令后面可以跟文件或者子目录,设置需要reset的文件,而不是整个branch。reset将当前分支的HEAD节点设到该commit,并且将暂存区设为提交该commit时的暂存区(staging area),新增的都会被删除。要注意的是删除暂存区这一条无论是soft也好hard也好还是默认的都会做。要reset到已经push的commit之前,非要这样做,使用revert(想想为什么?)。最常用的命令是git reset –hard HEAD.

如果想要将某个文件file从git add操作中移除,使用git reset (–mixed )HEAD file

《Git 高级用法 [原创]》

 revert跟reset不同的是,他并不向后/向前移动commit历史指针,而是直接创建新的commit历史,这个commit的内容就是将所有文件更改到之前的某个commit。这个一般用于public branch。(想一想为什么?)

 《Git 高级用法 [原创]》

 

checkout之前需要保证当前branch没有新的改动需要commit。checkout之后就把快照转为目的分支的快照。也可以是某个commit,使用checkout到某个之前的commit并不会删除这个commit后的commit,而只是把工作目录/快照转到当时commit的工作目录/快照,但也因为这个不会删除之后的commit,一旦做了改动,就相当于从原分支独立出来了。checkout不会改动暂存区的文件。用checkout到某个commit后使用reset –hard HEAD回到头部和用checkout -f回到头部效果是一样的。个人认为使用checkout回到过去并不是为了修改和增加新功能。

《Git 高级用法 [原创]》

 

2. git merge, git rebase

rebase的命令一般是git rebase somebranch。含义就是在本地分支开始点加上somebranch分支带来的commit。一般无法用于public branch,如果用了,本地的这个分支跟远程仓库里的分支将不会被当作同一个(想想为什么?),到时候也无法push回被rebase的分支,就算merge两条分支,也会产生同样的改动进行了两遍的问题。就算force push,也会让其他使用该分支的人感到疑惑。【那么怎么forcepush?gerrit中project-》list-》project-》access-》edit下面的当前权限-》edit-》refs/head/*的push后面的forcepush勾上即可。但要注意马上改回来】

feature分支rebase了master分支后的情形:

《Git 高级用法 [原创]》

master分支rebase了feature分支后的情形:

《Git 高级用法 [原创]》

  

merge的命令一般是git merge local remote,含义是将remote的commit整合到local,带来的变化通过一个merge commit实现。merge的过程会有冲突出现,可通过git status来查看需要修改的文件,修改完毕后通过git add file来提交修改,当git status干净时便merge成功。这个一般用于public branch。(想想为什么?)

《Git 高级用法 [原创]》

rebase和merge的区别:可以发现rebase没有动master而merge动了master。并且merge后feature分支也不存在了。

《Git 高级用法 [原创]》

 

总结一下,一般无法用于public branch 的命令用于私有branch都是很棒的。能使git log展示的commit 历史更加清楚易懂(没有merge commit)。

说到使commit历史更加清晰一致,就有必要用到git rebase -i commitpoint命令。将pick改为fixup能合并commit历史。这个commitpoint能是其他branch也能是本地的某个commit。i代表interactive。要想查看本分支与源的交叉点,使用git merge-base local source. 重新推送到repo时需要server端允许force push。

重新对以上做总结,一般主线推送一定要经过编译。未经过编译的版本均不能推送到主线分支。如果一定要做这种推送,建议新建该分支的test分支用以测试。并且对类似test分支给予force push的权力。到时候一旦编译通过/测试OK就可以在上面的rebase之后force push,然后git checkout 到主线后merge该test分支。

 

other advance usage of git command:

git log: (git shortlog)

–oneline, –graph, –decorate, –stat, -p, –pretty=format:”..%cn, %h, %cd”, -3, –after=”2016-09-18″, –before=”yesterday”, –since=””, –until=””, –author=””,

by message: –grep=”..”

by file updated: e.g. git log — file.sh, if not confused with any branch names.. can be git log file.sh

by contents: git log -S”hello”, 这命令是找出所有文件中增加了hello词汇的commit。

git log master..feature: 写出自分支出feature后所有feature的commit,git log feature..master: 写出自分支出feature后所有master的commit。可以很好的对比两者各自发展之类的。

git log –merges,

git log –no-merges这两个是分别展示与不展示merges的commit

 

git rev-parse tag 获取跟tag有关的commit的hash值

grammar:.. git push repo [+]srcbranch:dstbranch, 如果srcbranch留空,那么repo里的dstbranch就被清空(用空分支上传),这个[+]src:dst一般称为refspec,总是由第一个参数向第二个传送,无论push还是pull。

另外要注意的是git push origin master = git push origin/master = git push remotes/origin/master = git push refs/remotes/origin/master,这个结构在.git/refs下可以找到。

.git/config 修改后就能使用git fetch/push origin(repo name)来实现特定功能:语法同上grammar。这个+号意味repo即使不能fast-forward也要进行non-fast-forward update。然后fast-forward意思见下图: 

[remote "origin"]
    url = https://git@github.com:mary/example-repo.git
    fetch = +refs/heads/master:refs/remotes/origin/master
    push = refs/heads/master:refs/heads/qa-master

github是默认no-ff模式。比起fast forward,这种模式保留了branch信息。但实际上我们如果用git命令行去merge一般是ff模式,除非merge的两个branch存在3-way。这时ff模式不能merge,只能no-ff。

说了这么多,ff模式实际上就是将master移动至branch/master。而no-ff模式就是将master移动至新建的一个master上。上图:

《Git 高级用法 [原创]》

【ff模式】

《Git 高级用法 [原创]》

【no-ff模式】

 

《Git 高级用法 [原创]》

上图中的右图是github的工作模式。

关于HEAD后面跟各种数字有两个符号,~以及^,前者表向前数第几个,后者表示merge的第几个分支。HEAD~3, HEAD^2..

《Git 高级用法 [原创]》

use git reflog to check all the behavior have been done in local repo, everything done is recorded in it.

to goto any step in the history presented in reflog, use git checkout HEAD@{n}

——————-git vars——————–

to list all git vars by:

git config -l

some important vars:

user.name, user.email, push.defualt.

where in git 2.0, push.default’s value: 

  • matching means git push will push all your local branches to the ones with the same name on the remote. This makes it easy to accidentally push a branch you didn’t intend to.

  • simple means git push will push only the current branch to the one that git pull would pull from, and also checks that their names match. This is a more intuitive behavior, which is why the default is getting changed to this.

With a very large repo several people commiting to it everyday many times, need to setup git gc every night. /* garbage collection */

 

git can select to pull whatever depth/commits of a repo, by:

git clone https://github.com/sansna/dotconf . --depth=1

to fetch more commits from repo, by:

git fetch --depth=
# or
git fetch --unshallow

 

点赞