目录
基础提交
本地初始化git仓库,初始化后会有.git目录:
git init
向暂存区中添加文件(工作区->暂存区),然后提交到版本库(暂存区->版本库):
git add a.txt b.txt
git commit -m "add two files"
-m表示本次提交的说明,一般都要写。
全部文件add时,用:
git add .
删除文件时,add换为rm。
暂存区只是暂时存放,commit之后就空了哦!
版本回退
查看当前仓库状态:
git status
查看某个修改的文件(比如readme.txt)修改了什么,也就是和版本库里文件的差异:
git diff readme.txt
查看历史提交记录:
git log
会看到每次提交的commit id,想回退到某次提交:
git reset --hard <commit-id>
一般都用--hard,如果用--soft,仅撤销commit没撤销add。如下图:
commit-id很长,取前几位即可。除了指定具体的commit-id,你也可以用HEAD^或者HEAD~1表示上一个版本,HEAD^^或HEAD~2表示上上个版本,以此类推...
git reset --hard HEAD^
回退了之后,你想反悔怎么办?这时候git log就只有之前的记录了。这时候用
git reflog
它会查看你的每一次命令包括commit-id,你再git reset --hard就能回来了。
还有一种常见场景,commit后反悔了,想回到没有add时的状态,也即是保留修改。如上图用--mixed就可以:
git reset --mixed HEAD^
上面是版本回退,那状态回退如何做?如下。
撤销工作区修改(或恢复工作区删除的文件),让这个文件回到最近一次git commit或git add时的状态:
git checkout -- readme.txt
--很重要,没有--表示切换分支,后面讲。
撤销暂存区修改,修改放回到工作区:
git reset HEAD readme.txt
举个例子,你修改了readme.txt,还git add了,这时候你不想提交了,工作区修改也不想要了,你就可以:
git reset HEAD readme.txt
git checkout -- readme.txt
当然,你也可以直接git reset --hard HEAD。
小结一下:
- 工作区改错了,用 git checkout -- <file>
- 暂存区add错了,用 git reset HEAD <file>,暂存区代码回到工作区
- 撤销commit,用 git reset --hard/mixed/soft HEAD~,其中mixed是默认选项可省略,hard表示回到上一次commit,mixed表示回到工作区,soft表示回到暂存区。HEAD~可替换为具体的commit-id。
远程仓库
本地git仓库和远程git仓库(比如github)通过SSH加密,下载代码没要求,但要上传代码要将公钥id_rsa.pub中的文本粘贴到远程git仓库“Add SSH Key”配置中。私钥id_rsa在本地能够和公钥配对。
克隆远程仓库
如果你没有本地仓库,直接把远程仓库内容全克隆下来:
git clone git@github.com:alibli/test.git
关联本地仓库
假如你新建了一个远程空仓库和一个本地仓库,远程仓库也添加了公钥,那如何关联本地仓库:
git remote add origin git@github.com:alibli/test.git
origin代表远程仓库的名字。
然后将本地仓库的所有内容推上去:
git push -u origin master
即
git push <远程主机名> <本地分支名>:<远程分支名>
另外,公司里gerrit代码仓一般需要经过review才能提交,要用refs/for/
git push origin HEAD:refs/for/remote_branch_name
如果不需review,refs/for/替换为refs/heads/。
当本地分支名和远程分支名相同时,可省略只写本地分支名即可。master是本地默认主干分支,-u表示将本地master和远程master关联,以后就可以简化push命令:
git push
查看远程仓库
git remote -v
解除关联本地与远程仓库
git remote rm origin
origin是git remote -v获取的某个名字,如果你关联时候起的origin,就是origin。
切换远程分支
见下文分支管理。
查看某个文件修改人及修改内容
如果你想看远程仓库某个文件历史修改记录,具体修改人与修改内容。代码拉下来之后git log只能看commit信息,怎么做?
git blame <fileName>
git show <commitId> <fileName>
blame会查询某个文件的修改记录,按行排列,每行开头就是<commitId>。show会显示具体某次<commitId>某个<fileName>的修改内容。
分支管理
创建、合并、删除分支
创建并切换至dev分支:
git checkout -b dev
加了-b表示创建+切换,相当于
// 创建
git branch dev
// 切换
git checkout dev
查看本地分支,带*的为当前分支
git branch
查看所有分支,含远程remotes分支
git branch -a
如果你想切换到远程某分支,比如分支名字叫remotes/origin/develop,并且想在本地也建立一个分支dev与之关联:
git checkout origin/develop -b dev
或者简写为
git checkout develop
这样会创建一个本地develop分支与远程 remotes/origin/develop 内容相同。
在dev分支一顿修改并commit。
切回master分支:
git checkout master
切换分支也可用:
// 切换到dev分支
git switch -c dev
// 切换到master分支
git switch master
你会发现修改都不见了,那是当然的,因为你的修改都在dev分支。想合并?用
git merge dev
表示将dev分支内容合并到master分支上。
如图,只是指针的移动哦!
删除dev分支:
git branch -d dev
关于git merge的深入讨论
更深入一点,讨论git merge,默认是fast-forward(快进),我们可以指定--no-ff禁用fast-forward,区别如下:
fast-forward:
--no-ff:
第一,可见no-ff可以保留bugfix分支所有信息,方便问题回溯。第二,master分支只多一次commit,上库是不是特方便呢!
解冲突
如果出现了这种情况:
也就是feature1分支和master分支各commit了一个提交。这时候你在master分支上执行
git merge feature1
合并分支,可能会出现冲突。也就是两个分支同时改了一个文件的一个位置,就会发生冲突,因为它并不知道你想保留哪个,所以需要手动解冲突。先git status查看哪个文件冲突,然后打开那个文件,会看到类似这种格式的冲突:
<<<<<<< HEAD
master分支文件内容
=======
相同位置feature分支文件内容
>>>>>>> feature1
删掉一个即可。然后git add,git commit。
然后你可以看下log,会以graph方式显示你的合并过程。
git log --graph --pretty=oneline --abbrev-commit
最后删除feature1分支:
git branch -d feature1
git stash某分支临时保存与恢复
开发中经常遇到,你本地有好多分支,这时候这个分支问题没修复完,那个分支着急解决。这时候如何临时保存当前分支状态和如何恢复呢?
很简单,不用非要commit。`git stash` 命令用于临时保存未提交的工作目录和暂存区的更改,以便你可以在一个干净的状态下切换到其他分支或处理其他任务。这在你需要切换分支或处理紧急事务时非常有用,而不想提交未完成的更改。以下是一些常用的 `git stash` 命令:
1. 保存工作区和暂存区的更改:
git stash save "Your stash message"
这会将你的工作区和暂存区的更改保存在一个临时存储区。你可以为这个存储的状态添加一个描述性的消息。直接git stash也可以。
2. 查看已保存的存储状态:
git stash list
这会列出所有已保存的存储状态,每个状态都有一个唯一的标识(stash@{n})。
3. 应用某个存储状态:
git stash apply stash@{n}
这会将指定的存储状态的更改应用到你的工作目录和暂存区,但不会从存储列表中删除这个状态。
4. 应用并删除某个存储状态:
git stash pop stash@{n}
这会将指定的存储状态的更改应用到你的工作目录和暂存区,并从存储列表中删除这个状态。
5. 删除某个存储状态:
git stash drop stash@{n}
这会从存储列表中删除指定的存储状态,但不会影响你的工作目录和暂存区。
6. 清除所有存储状态:
git stash clear
这会删除所有已保存的存储状态。
cherry-pick
我们每次commit只提交了一部分代码,比如只修复了一个问题。那么一个问题在dev1分支上修复了,想在dev2分支上也修复,但是这俩分支总体代码差异大,能不能快速修复这单个问题呢?可以,cherry-pick登场!!
先在dev1分支上查看所有commit,git log确定需要的那次commit的<commit-id>,切换到dev2分支,执行
git cherry-pick <commit-id>
解冲突,ok了。
本地dev分支推送到远程dev分支
方式一:
多人开发时候你本地的dev分支要推到远程仓库,经常遇到修改同一文件同一位置冲突的场景,所以一般都是先pull再解冲突再推上去。
// 关联分支
git branch --set-upstream-to=origin/dev dev
// 拉代码
git pull
// 解冲突 ... 可能需要手动
// commit
git commit -m "fix conflict"
// push
git push origin dev
方式二:
上述方式查看log会看到多个分支和merge节点,很乱。能不能一条直线?可以,rebase登场!
// rebase将你的提交置于远程拉取的提交之后(变基)
git pull --rebase
// 解冲突 ... 可能需要手动
git add .
git rebase --continue
再git pull --rebase之后,与远程代码同步有冲突会提示,如果你想撤销rebase,执行
git rebase --abort
回到没有pull时的状态。
其他小技巧
打标签
commit-id都是一串数字不好记,我们可以打标签:
git tag v0.1 <commit-id>
git tag v0.2
以后v0.1就代表那个commit-id的tag,直接git tag v0.2后面不加commit-id表示在最新的commit-id上打标签v0.2。也可以加-m "标签信息"。
查看所有标签:
git tag
查看某个标签:
git show v0.1
tag推送到远程:
// 推送一个
git push origin v0.1
// 推送所有
git push origin --tags
tag从远程删除:
// 本地删除
git tag -d v0.1
// 推送远程
git push origin :refs/tags/v0.1
忽略特殊文件
很常用,有很多文件不需要提交到远程,比如自动生成的文件(缩略图,.class等),编译中间产物,可执行文件,敏感信息文件等等。
修改.gitignore文件即可,可以用通配符,举例:
# 排除单个文件
a.txt
# 排除所有.开头的隐藏文件:
.*
# 排除所有.class文件:
*.class
# 但不排除.gitignore和App.class:
!.gitignore
!App.class
配置了这个文件,你还想强制添加,用git add -f <file>
git add -f App2.class