1. Git中常用操作之间的关系:
- Git中版本的表示方法(Specifying Revisions):
2. 账号设置
- 登录GitHub
打开网页https://github.com/点击右上角的链接登陆按钮”Sign in”。
- GitHub帐号设置
登录成功后,点击右上角你的帐号“Settings“->”SSH and GPG Keys”。
点击”New SSH Key”添加新密钥,密钥为ssh公钥文件的内容,一般保存在电脑本地home目录下,即”cat ~/.ssh/id_rsa.pub”,如下所示:
3. 克隆代码仓库
- 本地仓库(Local Repository):
Git的好处之一便是代码本地化,远端仓库Remote Repository被克隆到本地之后形成Local Repository,用户便能看到项目相关的所有代码。
例如:克隆”Linux”内核代码仓库
$ git clone git@github.com:torvalds/linux.git
Note: 如果某个仓库配置的submodule,克隆时的命令参数为:
$ git clone --recurse-submodules xxxx
- 配置多个远端仓库
在clone某个远端仓库之后,该仓库被自动配置为当前Local Repo的fetch/push target
$ git remote -v
origin git@github.com:torvalds/linux.git (fetch)
origin git@github.com:torvalds/linux.git (push)
但是,有些Git应用场景需要配置多个Remote Repo,即一个Local Repo可以fetch/push到多个Remote Repo:
$ git remote add <remote-name> <URL>: 添加远程仓库主机
$ git remote show <remote-name>: 查看远程仓库详细信息
$ git remote rm <remote-name>: 删除远程仓库主机
示例如下:
$ git remote -v
origin git@github.com:torvalds/linux.git (fetch)
origin git@github.com:torvalds/linux.git (push)
vlinux git@github.com:elvishuang/linux.git (fetch)
vlinux git@github.com:elvishuang/linux.git (push)
4. Setup Git hooks
origin git@github.com:torvalds/linux.git (fetch)
origin git@github.com:torvalds/linux.git (push)
vlinux git@github.com:elvishuang/linux.git (fetch)
vlinux git@github.com:elvishuang/linux.git (push)
Git hook是用来做sanity check,以及每当用户有push动作时,自动生成一个code review链接供项目相关人员进行代码review。
5. 创建本地工作区(Workspace)
- 如果确切知道目标tag
$ git tag –l | grep target-tag
$ git checkout –b branch-name target-tag
- 多个remote时的仓库更新
一旦远程主机的版本库有了更新(Git术语叫做commit),需要将这些更新取回本地,即git fetch。Git fetch只是取回远程代码仓库的更新, 取回的代码对本地workspace中已开发代码没有任何影响。
$ git fetch <remote-name>
或者
$ git remote update
默认情况下,git fetch取回所有分支(branch)的更新。如果只想取回特定分支的更新,可以指定分支名。
$ git fetch <remote-name> <branch-name>
比如,取回origin主机的master分支。
$ git fetch origin master
- 代码分支
所取回的更新,在本地仓库中以"远程主机名/分支名"的形式存储。比如origin主机的master分支的本地化名字为origin/master。
git branch命令的-r选项,可以用来查看远程分支,-a选项查看所有分支。
$ git branch -r
origin/master
$ git branch -a
* master
remotes/origin/master
上面命令表示,本地主机的当前分支是master,远程分支是origin/master。
取回远程主机的更新以后,可以在它的基础上,使用git checkout命令创建一个新的分支。
$ git branch -D branch-name (预先删除可能存在的同名branch-name)
$ git checkout -b branch-name origin/master
该命令表示,在origin/master的基础上,创建一个新分支。
此外,也可以使用git merge命令或者git rebase命令,在本地分支上合并远程分支。
$ git merge origin/master
或者
$ git rebase origin/master
上面命令表示在当前分支上,合并origin/master。
- 子模块Submodule相关操作
应用场景:大型代码仓库在组织框架上往往把某些较小的模块独立出来形成子模块,也有可能在某个工程中需要使用其他工程或者第三方开发的库。
添加子模块:
$ git submodule add <Repo URL> <Local Path>
该操作将更新仓库本地的隐藏配置文件.gitmodule,其中包含所有子模块的相关路径配置。
仓库更新或者切换分支之后需要更新子模块,注意:子模块仓库要先初始化才能更新。
$ git submodule init
$ git submodule update –init --recursive
6. 代码修改
在本地仓库中修改代码或者Bug之后,此时所有改动只是保存在workspace中,首先需要将workspace中的改动保存到Local Repo中。
$ git add .
$ git commit –a
有些情形下,代码经历了多次修改才最终成型,用户在本地仓库中也commit多次,但是却希望把当前功能的多次commit合并成一个。也就是User Manual中提到的:To squash several commits to one, use this command:
$ git rebase –i BaseCommitId
特殊情况:当只剩下最后两个commit时, 由于已不再存在BaseCommitId, 所以此时”git rebase –i”便不再起作用,合并最后两个commit采用如下命令:
$ git reset --soft HEAD^1
$ git commit --amend
关于git reset --soft <commit>:
It did not touch the index file nor the working tree at all (but resets the head to <commit>). This leaves all your changed files "Changes to be committed", as git status would put it.
如果在rebase过程中出错,如需要修改rebase动作:
$ git rebase --edit-todo
然后让rebase动作继续执行下去:
$ git rebase --continue
该命令同样适用于不同branch之间的git rebase –onto之后的冲突解决。
如果还需要额外修改commit msg,使用命令:
$ git commit --amend
$ git commit --amend --author="elvis huang <sduhuangshuai@gmail.com>"
另外,某些情形下的代码改动是由于移植代码补丁patch。
$ git format-patch HEAD^
该命令会将当前分支的最新节点与前一个节点之间的diff生成代码补丁。其中HEAD表示当前分支的最新节点,“^”表示要生成的补丁patch数目,即意味着^可以有多个。
$ git format-patch HEAD^^
会生成以当前最新节点为起点的最近两次commit之间的代码补丁。
代码补丁(Apply Patch):应用代码补丁时,命令为:
$ git apply --ignore-space-change --ignore-whitespace xxx.patch
仅查看当前补丁patch的统计信息:
$ git apply --stat xxx.patch
cherry-pick:不同分支间的同步命令
挑选指令(git cherry-pick) 实现commit在新的分支上"重新放置",其含义就是从众多的提交中选出一个提交应用到当前分支上。该命令需要提供一个commit ID作为参数,操作过程相当于将该commit导出为补丁文件,然后在当前分支的HEAD上重放,形成无论内容还是提交说明都与之前一致的一个新commit。
NAME
git-cherry-pick - Apply the changes introduced by some existing commits
SYNOPSIS
$ git cherry-pick <commit>…
$ git cherry-pick --continue
$ git cherry-pick --quit
$ git cherry-pick --abort
EXAMPLE
将master分支上的改动commit 10和11同步到当前分支branch3上,效果图如下所示:
$ git cherry-pick 0bda20e 1a04d5f
7. 提交本地代码修改到远程仓库
- 代码提交前的准备工作
以一个典型的开发情形为例,在某个时间点t1项目启动:需要做一个feature,而且能预期到feature开发周期比较长,但是为了feature的稳定开发,此时单独在git上拉出一个分支来测试新开发的代码,并且在开发周期内不会与主分支进行同步,防止主分支上其他新功能与当前feature混淆测试引入debug困境。当该feature开发完成之后,时间点已经到了t2,这时候主分支上节点更新很多,在提交本地代码修改到远程仓库(git push)之前,需要将当前分支rebase到主分支的最新节点上,syntax:
$ git rebase --onto new_origin old_origin local_branch_name
以linux为例:
$ git rebase –onto remotes/origin/linux_security_enhance
39a8804 sandbox_patch
将从节点39a8804拉出来的本地开发分支sandbox_patch rebase到origin/linux_security_enhance分支上去。
注意:如果在rebase过程中产生冲突,需要用git mergetool去解决冲突之后才能继续rebase。默认的mergetool为vimdiff,界面及常用操作如下:
$ git mergetool
From left to right, top to the bottom:
LOCAL – this is file from the current target “rebase --onto” branch.
BASE – common ancestor, how file looked before both changes.
REMOTE– your modified file content to be merged.
MERGED – merge result, this is what gets saved in the repo
Let’s assume that we want to keep the REMOTE change. For that, move to the MERGED file (Ctrl + w, j), move your cursor to a merge conflict area and then:
:diffget RE
This gets the corresponding change from REMOTE and puts it in MERGED file.
You can also:
:diffg RE “get from REMOTE”
:diffg BA “get from BASE”
:diffg LO “get from LOCAL”
:diffupdate “update file diff window after :diffget”
Save the file and quit (to write and quit multiple files is :wqa).
- Git push命令
git push命令用于将本地仓库的代码更新和改动,推送到远程仓库完成代码提交功能。
$ git push <远程主机名> <本地分支名>:<远程分支名>
注意:分支推送顺序的写法是 <来源地>:<目的地>,所以git pull是<远程分支>:<本地分支>,而git push是<本地分支>:<远程分支>。
如果省略远程分支名,则表示将本地分支推送与之存在"追踪关系"的远程分支(通常两者同名),如果该远程分支不存在,则会被新建。
$ git push origin master
该命令会将本地的master分支推送到origin主机的master分支。如果后者不存在,则会被新建。
如果省略本地分支名,则表示删除指定的远程分支,因为这等同于推送一个空的本地分支到远程分支。
$ git push origin :master
等同于:
$ git push origin --delete master
该命令表示删除origin主机的master分支。
如果当前分支与远程分支之间存在追踪关系,则本地分支和远程分支都可以省略。
$ git push origin
该命令表示,将当前分支推送到origin主机的对应分支。
如果当前分支只有一个追踪分支,那么主机名都可以省略。
$ git push
如果当前分支与多个主机存在追踪关系,则可以使用”-u, --set-upstream”选项指定一个默认主机,这样后面就可以不加任何参数使用git push。
$ git push --set-upstream origin master
该命令将本地的master分支推送到origin主机,同时指定origin为默认主机,后面就可以不加任何参数使用git push了。
不带任何参数的git push,默认只推送当前分支,这叫做simple方式。此外,还有一种matching方式,会推送所有有对应的远程分支的本地分支。Git 2.0版本之前,默认采用matching方法,现在改为默认采用simple方式。如果要修改这个设置,可以采用git config命令。
$ git config --global push.default matching
或者
$ git config --global push.default simple
还有一种情况,就是不管是否存在对应的远程分支,将本地的所有分支都推送到远程主机,这时需要使用--all选项。
$ git push --all origin
该命令表示,将所有本地分支都推送到origin主机。
如果远程主机的版本比本地版本更新,推送时Git会报错,要求先在本地做git pull合并差异,然后再推送到远程主机。但是,如果你一定要推送,可以使用--force选项。
$ git push --force origin
该命令使用--force选项,结果导致远程主机上更新的版本被覆盖。除非你很确定要这样做,否则应该尽量避免使用--force选项。
最后,git push不会推送标签(tag),除非使用--tags选项。
$ git push origin –tags