前言
git这个版本控制工具,早在两三年前我就开始使用了。不过后来换了新东家后,又开始变成了svn,最近又切成git了。
通过近期的使用,遇到了一些坑,遂引发此文,以作记录
issue:某个commit整体不要了,想重置?add多了,想撤销?某个文件有问题,想还原到某次commit时的状态?想push到另外的远程仓库?什么!这行代码是哪个鬼加进去的,引起了bug?咦,这个bug,在好几个版本都存在,是哪次commit引起的?… 本文都能找到答案
注:可以通过上面的目录,来选取自己感兴趣的内容,进行查看
下载安装
链接:
https://git-scm.com/download 下载首页
https://git-scm.com/download/win
https://git-scm.com/download/linux
https://git-scm.com/download/mac
https://git-scm.com/downloads/guis/ GUI工具
mac下还可以通过brew安装,再配合zsh的git短命令插件,来玩。
下载安装好后,打开命令行,运行:git --version
查看版本号,以确保git可用。如提示找不到命令git,请将其安装目录下的可执行文件(git.exe 或 就叫git)父目录路径,添加到环境变量中。
生成密钥
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
命令:
ssh-keygen -t rsa -C "[email protected]"
回车后会弹出提示:
Generating public/private rsa key pair.
Enter file in which to save the key (/Users/stone/.ssh/id_rsa):
意思是:创建公钥/私钥文件,请键入文件的保存地址。默认直接回车就会生成在/Users/stone/.ssh/ 下,私钥文件名:id_rsa,公钥文件名:id_rsa.pub。
密钥目录,一般都是在 users>user>.ssh 下。windows中为:C:\Users\Administrator.ssh
也可以在输入创建命令时直接指定目录:
ssh-keygen -t rsa -f ~/.ssh/id_rsa.github -C "[email protected]"
回车后,会提示:
Enter passphrase (empty for no passphrase):
意指,输入密码。不输,也可以,直接回车就行。然后会提示让你再输入一次密码。输入后,再回车,就会生成相应的密钥文件,在指定的目录位置。
注: 一般,可能我们需要多种git密钥,比如GitHub需要一个,公司需要一个,其它… 所以有必要指定不同的密钥文件名字加以区分。
查看公钥
一般我们要把公钥,上传到服务器。
找到.pub结尾的公钥文件。
以命令行打开(mac/linux 用cat命令),或用文本文件打开,复制出来,上传即可
类似内容:
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDfCwawUX/Milxu29sH5kZPFWWPoe/
… aa86799@163.com
配置
设置user name 、email
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
设置Git的user name和email,用于显示在版本提交记录中:
git config --global user.name "stone"
git config --global user.email "[email protected]"
--后可选 global、system、local
global 影响范围:系统用户下全局
system 影响范围:系统下全局
local 影响范围:当前项目下
我一般的做法是,进入项目目录后,采用local来配置。因为对应多个git服务端,我想设置不同的邮件和名字。
更多参见自定义 Git - 配置 Git
配置.ssh/config
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
上面的命令执行后,会在.ssh目录下生成一个config文件。当然,不执行命令,也可以在其下,手动创建一个名为config的空文件。
如下,我配置了github和oschina:
Host github.com
IdentityFile ~/.ssh/id_rsa.github
User git
Host oschina.net
IdentityFile ~/.ssh/oschina_id_rsa
User git
一些可选配置参数说明:
Host github.com #主机名
IdentityFile ~/.ssh/id_rsa.github #公钥文件
User git #连接服务器的用户名
hostname www.github.com #服务器ip地址,也可以是域名
Port 5566 #服务器端口号
RSAAuthentication yes # 采用RSA加密认证
PubkeyAuthentication yes #采用公钥认证
配置后,在本地与server的交互时,git才知道用哪一种具体的config。
git仓库
本地新建仓库
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
进入本地文件目录,一般就是某个项目的工程目录下,执行:
git init
会生成一个.git目录
关联远程仓库
比如关联github。
需要先在github上建立一个repository,比如名为:MyActionBar
再进入项目根目录,键入命令,关联远程仓库:
git remote add origin [email protected].com:aa86799/MyActionBar.git
git clone远程仓库
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
比如clone 一个github上的项目,命令:
git clone https://github.com/googlesamples/android-architecture.git
#直接clone出名为android-architecture的项目
后跟一个指定的项目名字:
git clone https://github.com/googlesamples/android-architecture.git myArchi #clone出一个名myArchi的项目
配置本地user name 、email
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
配置命令:
git config --local user.name "stone"
git config --local user.email "aa86799@163.com"
会在.git中的config文件中添加如下内容:
[user]
name = stone
email = aa86799@163.com
git 别名,短命令
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
如果不想每次都输入完整的 Git 命令,可以通过 git config 文件来轻松地为每一个命令设置一个别名。如:
$ git config --global alias.co checkout
$ git config --global alias.br branch
$ git config --global alias.ci commit
$ git config --global alias.st status
再例如,为了解决取消暂存文件的易用性问题,可以向 Git 中添加你自己的取消暂存别名:
git config --global alias.unstage ‘reset HEAD --‘
这会使下面的两个命令等价:
$ git unstage fileA
$ git reset HEAD -- fileA
注:别名时,不能把git也放进去。当然有些脚本插件是可以的。
后文还有diff与merge工具的相关配置
版本控制操作
以下罗列一些常用的命令操作与选项。
下文中, 符号[ ]表示,命令后跟的内容,实际使用时,不需要符号[ ]。
branch 分支
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
仓库建好后,默认为master分支
分支的好处:
如在svn中,每次开发一个新版本,都要新创建一个分支的目录,复制所有项目文件进去;而在git中只要创建一个新的分支,会瞬间创建成功。
还可以创建本地分支,进行自己的想要一些改动,只要不push到远程,就不会影响服务端项目。
分支相关常用命令:
- 查看本地所有分支:
git branch
当前所在分支前面,会有一个星号(*)
- 查看本地和远程所有分支:
git branch -a
- 显示分支更多信息
-v选项,会输出分支的最后提交记录
git branch -v
- 创建本地分支(从当前分支最后commit):
git branch [newBranch]
git checkout -b [newBranch] #创建并切换到新分支
git checkout -b [newBranch 9510eee]#从当前分支的某次commit记录为9510eee处,创建新分支,并切换到新分支
git branch [newBranch 9510eee] #从commit记录为9510eee处,创建新分支
- 创建与远程分支相关联的本地分支:
#检出远程master分支 到 本地分支 newBranch
git branch [newBranch remote/master]
git checkout -b [newBranch remote/master] #创建并切换到新分支
- 删除分支:
git branch -d [specifiedBranch]
- 切换分支:
git checkout [branchName]
注:分支切换时,要注意,如果本地有修改未commit或stash,需要先commit或stash
- 分支合并
这部分内容放到下文#merge中
tag 标签
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
一般,项目的每个release稳定版,都要求有一个tag标签
常用命令,
- 显示当前所有标签:
git tag
- 查找标签
git tag -l ‘v1.8.5*‘ #查找前缀为v1.8.5的所有分支
- 创建标签,并使用-m 添加说明信息:
git tag -a [v1.1] -m [v1.1 release version] #可以没有-m,但一般还是加上好
若想对过去的某次commit打tag:
git tag -a [v0.8 077d7ad] -m ["v0.8"] #-m时有没有双引号都可以
- 共享标签
默认情况下,git push 命令并不会传送标签到远程仓库服务器上。 在创建完标签后你必须显式地推送标签到共享服务器上。 这个过程就像共享远程分支一样,你可以运行
git push origin [tagname]#推送某个tag到origin
git push origin --tags #把所有不在远程仓库服务器origin上的标签全部传送
- 删除标签:
git tag -d [tagName] #删除本地
git tag -d -D [tagName] #强制删除本地tag
git push [origin] --delete tag [tagName]#删除远程origin上的tag
- 从一个标签检出一个新分支:
git checkout -b [branch_name tag_name]
status 文件版本状态
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- 查看项目中文件的变动状态:
git status
在输出信息中,会看到(这里只记录了开头修饰词):
new file 已加入版本记录,但未commit
modified 修改了版本记录中某文件
deleted 删除了版本记录中某文件
Untracked files 未加入版本记录(追踪)
…
- 状态简览
git status -s
这里引用一个官方示例,输出如下:
$ git status -s
M README #M在右,前面空了一个字符位置
M Rakefile #M在左
A lib/git.rb
M lib/simplegit.rb
?? LICENSE.txt
说明:
?? —— Untracked file,未add到版本记录
A —— 已add (详见下面的add命令)
R —— rename,被重命名了
左M —— 新的修改,已add到版本记录
右M —— 新的修改,未add到版本记录,如要提交该修改,必须先add
左D 、右D —— 类似M,指文件删除记录,是否add到版本记录中
add 加入版本记录
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
将文件加入版本记录。配合status命令,将需要加入版本记录的,有选择的加入。
git add //后跟目录路径,或某个具体文件路径
- -p选项
可以将一个文件的多处修改,分割成多个块来多次提交。
git add -p [flie]
与-i命令中的暂存补丁效果一样。
- -i或–interactive选项
使用该选项,Git 将会进入一个交互式终端模式,基本上与 git status 是相同的信息。
显示类似下面的东西:
git add -i
staged unstaged path
1: +1/-1 nothing a
2: unchanged +1/-0 b
*** Commands ***
1: status 2: update 3: revert 4: add untracked
5: patch 6: diff 7: quit 8: help
What now>
它将暂存的修改列在左侧,未暂存的修改列在右侧
在这块区域后是命令区域。 在这里你可以做一些工作,包括暂存文件、取消暂存文件、暂存文件的一部分、添加未被追踪的文件、查看暂存内容的区别。
a.暂存与取消暂存文件
输入2或u,会提示需要暂存(即add)哪个文件,输入数字索引即可暂存。
输入3或r,会提示要取消暂存哪个文件
若在update或revert状态时,什么也不选,直接输入回车,就会将之前的输入操作执行完,并退出当前状态。
b.暂存补丁
输入5或p。会输出第一个未暂存文件的diff信息,类似git add -p [file]
的块选择,如:
diff --git a/b b/b
index e69de29..01f02e3 100644
--- a/b
+++ b/b
@@ -0,0 +1 @@
+bbb
\ No newline at end of file
Stage this hunk [y,n,q,a,d,/,e,?]?
输入?可查看更多选项。输入y暂存。然后会继续显示下一个未暂存文件的diff信息…
ignore 忽略文件
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
将不需要加入版本记录的文件,进行ignore操作。
需要在仓库目录下建立一个 .gitignore 文件(其与.git是平级的)
配置语法:
以斜杠“/”开头表示目录,以防止递归;可以以”/”结尾指定目录
以星号“*”通配多个字符;
以问号“?”通配单个字符
以方括号“[]”包含单个字符的匹配列表;如果在方括号中使用短划线分隔两个字符,表示所有在这两个字符范围内的都可以匹配(比如 [0-9] 表示匹配所有 0 到 9 的数字)
以叹号“!”表示不忽略(跟踪)匹配到的文件或目录;
git 对于 .ignore 配置文件是按行从上到下进行规则匹配的,意味着如果前面的规则匹配的范围更大,则后面的规则将不会生效
rm 移除文件跟踪
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
要从 Git 中移除某个文件,就必须要从已跟踪文件清单中移除(确切地说,是从暂存区域移除),然后提交。 可以用 git rm 命令完成此项工作,并连带从工作目录中删除指定的文件,这样以后就不会出现在未跟踪文件清单中了。
- 不再追踪暂存区文件
如现有一个新文件PROJECTS.md,刚刚进行了add,还未提交过。
如果只是简单地从工作目录中手工删除(rm)文件,运行 git status 时就会在 “Changes not staged for commit” 部分(也就是 未暂存清单)看到:
$ rm PROJECTS.md
$ git status
On branch master
Your branch is up-to-date with ‘origin/master‘.
Changes not staged for commit:
(use "git add/rm <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
deleted: PROJECTS.md
no changes added to commit (use "git add" and/or "git commit -a")
然后再运行 git rm 记录此次移除文件的操作:
$ git rm PROJECTS.md
rm ‘PROJECTS.md‘
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
deleted: PROJECTS.md
下一次提交时,该文件就不再纳入版本管理了。
如果删除之前修改过并且已经放到暂存区域的话,则必须要用强制删除选项 -f(译注:即 force 的首字母)。 这是一种安全特性,用于防止误删还没有添加到快照的数据,这样的数据不能被 Git 恢复。场景:对于新文件,刚进行了add,然后又修改了该文件,然后想 用git rm 取消追踪,就需要-f选项了。
- 从 Git 仓库中删除版本追踪
另外一种情况是,我们想把文件从 Git 仓库中删除(亦即从暂存区域移除),但仍然希望保留在当前工作目录中。 换句话说,你想让文件保留在磁盘,但是并不想让 Git 继续跟踪。 当你忘记添加 .gitignore 文件,不小心把一个很大的日志文件或一堆 .a 这样的编译生成文件添加到暂存区时,这一做法尤其有用。 为达到这一目的,使用 –cached 选项:
git rm --cached -r README
git rm 命令后面可以列出文件或者目录的名字,也可以使用 glob 模式(指shell所使用的简化了的正则表达式)。 比方说:
git rm log/\*.log
注意到星号 * 之前的反斜杠 \, 因为 Git 有它自己的文件模式扩展匹配方式,所以我们不用shell 来帮忙展开。 此命令删除 log/ 目录下扩展名为 .log 的所有文件。
类似的比如:
git rm *~ 该命令为删除以 ~ 结尾的所有文件。
mv 移动文件,重命名
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
比如运行了下面三条命令:
$ mv README.md README
$ git rm README.md
$ git add README
Git 会意识到这是一次改名操作。
可以使用如下一条命令,来进行改名操作
git mv [file_from file_to]
commit 提交
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- 已add文件,commit
所有改动过的且已加入版本记录的文件,可以使用commit来提交到本地仓库。
git commit -m [message] [file1] [file2]
可以不写commit信息,一般还是建议写。
commit哪些文件是可以指定的,中间用空格分隔,需要文件的完整路径。
若不指定具体文件,默认为仓库下所有改动过的且已加入版本记录的文件
- 之前已在版本记录中,修改后未add文件,想直接commit
git commit -a -m [message] [file1] [file2]
添加-a选项,即可
- 撤销操作
有时候我们提交完了才发现漏掉了几个文件没有添加,或者提交信息写错了。 此时,可以运行带有 –amend 选项的提交命令尝试重新提交:
git commit --amend
例如,你提交后发现忘记了暂存某些需要的修改,可以像下面这样操作:
$ git commit -m ‘initial commit‘
$ git add forgotten_file
$ git commit --amend
最终你只会有一个提交 —— 第二次提交将代替第一次提交的结果。
注:其他撤销例子见reset
cherry-pick 重新提交历史记录
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
用于将已经存在的commit 记录进行 再次提交。
git cherry-pick c9f98b9
如果有冲突,解决冲突后,先add,再commit
stash 储存
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- 有时,突然来个紧急任务,需要切换分支,又不想为此单独提交一次。就可以使用stash命令。git会将未完成的修改保存到一个栈上,而你可以在任何时候重新应用这些改动。命令:
git stash
git stash save #与上等价
之后,工具目录自动切换成上次提交时的状态。
- 查看存储栈
git stash list
类似输出:
[email protected]{0}: WIP on master: 049d078 added the index file
[email protected]{1}: WIP on master: c264051 Revert "added file_size"
[email protected]{2}: WIP on master: 21d80a5 added number to log
- 应用储存的任务
如上面说到,在其他分支改完问题了,再切换之前分支,来应用储存:
git stash apply stash@{2} #如果不指定哪一条stash,默认为最近的,即[email protected]{0}
如果,有多个储存,应用一个后(若有冲突,先解决冲突),先将修改的add进暂存区,再apply其它的。
- 移除储存
git stash drop [email protected]{0}
- 清除所有储存
git stash clear
- 不储存已add的文件
git stash --keep-index
- 储存从未add的文件
默认情况下,git stash 只会储藏已经在索引(版本记录)中的文件。
若要储藏任何创建的未跟踪文件:
git stash -u
git stash --include-untracked #与-u等价
- 交互式指定要储存的文件
git stash --patch
- 从储存创建一个分支
git stash branch testchanges stash@{1}
push 推送
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- 完整push命令:
git push [origin localMaster:master]#将本地分支localMaster推送到远程仓库origin的master上
- 省略远程分支
git push [origin localMaster]#将本地分支localMaster推送到远程仓库origin上,如果远程不存在,则创建
- 省略本地分支
git push [origin :master]#推荐一个空分支到origin/master,当然origin/master就为空了。
- push时指定当前分支与远程关联
git push --set-upstream [origin master] #以后使用git push就可以了
- 如果当前分支与远程分支之间存在追踪关系,则本地分支和远程分支都可以省略。
git push
- 指定push的默认远程主机
git push -u [origin master]#以后再使用git push,即可。与第4点操作效果类似。
- 删除远程分支
git push [origin] --delete [master]#删除origin/master分支
- push所有本地分支到远程
git push --all [origin]
- 覆盖远程分支
git push --force [origin] #这是一个危险的命令,一般不要这么做
- 推送标签tag
git push [origin] --tags
注:push时,如果远程有更新,会失败。需要先pull,或使用rebase模式;若有冲突,需要解决冲突。关于冲突见#merge
fetch 获取remote最新信息
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
拉取远程仓库上的所有新的信息。
git fetch [remote-name] #如果只关联了一个远程仓库,可以不用显式指定远程仓库名
- -p选项
与pull中的-p意义一样。
pull 拉取
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
从远程获取最新版本并merge到本地
1.仅使用如下命令,表示拉取与当前分支关联的远程分支最新版本,并合并:
git pull
2.再比如,从远程仓库origin的newb分支,合并到master分支:
git pull [origin newb:master] #不写:master 表示与当前分支合并
3.使用–rebase选项
git pull --rebase [origin newb:master] #rebase模式pull
4.-p选项
默认情况下,git pull 不会在拉取远程分支的时候,删除对应的本地分支。因为,可能其他人误删除了远程分支。如果要相应的在拉取时也删除本地对应分支:
git pull -p #
pull时,可能有冲突,关于冲突见#merge
reset、checkout 重置
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- HEAD
HEAD 是当前分支引用的指针,它总是指向该分支上的最后一次提交。 这表示 HEAD 将是下一次提交的父结点。 通常,理解 HEAD 的最简方式,就是将它看做 你的上一次提交 的快照。
- 撤销add
例如,你已经修改了两个文件并且想要将它们作为两次独立的修改提交,但是却意外地输入了 git add * 暂存了它们两个。 如何只取消暂存两个中的一个呢? git status 命令提示了你:
$ git add *
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
renamed: README.md -> README
modified: CONTRIBUTING.md
提示中就说了,可以用如下命令来撤销某个add:
$ git reset HEAD CONTRIBUTING.md
这样,并不会丢失 CONTRIBUTING.md 文件的修改。
- –hard选项
将当前分支,重置到某次提交(只要是reglog中存在的都可以)。
git reset --hard 1a410efbd13591db07496601ebc7a059dd55cfe9
- 用checkout命令来重置提交或文件
- 重置文件
如有一个已在版本记录中的文件a,现在做了修改,然后add,突然又觉得它的修改不需要了,想重置回到最近记录的样子,可用命令:
- 重置文件
git checkout [HEAD a] #HEAD指代最新提交,当然也可用SHA-1值。
如果将文件a恢复到上上个版本,只需要将HEAD换成上上个commit的SHA-1值。这时,通过git status,看到文件a被修改了,且自动add。
2.重置commit
就是将当前分支重置到某次commit,同时会生成一个临时分支,并切换到这个临时分支上。
git checkout [077d7ad] #commit记录:077d7ad
如图:
这里还提示你可以后跟 -b 新分支名,来从该commit检出一个新分支。
log 日志,提交历史
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
查看提交历史
git log
会按提交时间列出所有的更新,最近的更新排在最上面。 这个命令会列出每个提交的 SHA-1 校验和、作者的名字和电子邮件地址、提交时间以及提交说明
- –oneline选项
一行输出,只显示SHA-1 和 提交消息
git log --oneline
- –all选项
显示所有分支的历史记录
git log —all
- -p选项
一个常用的选项是 -p,用来显示每次提交的内容差异(会输出diff差异)。 你也可以加上 -2 来仅显示最近两次提交:
git log -p -2
- –stat选项
查看每次提交的简略的统计信息
git log --stat
比如,输出
- –shortstat
只显示 –stat 中最后的行数修改添加移除统计
git log --shortstat
- –name-only
仅在提交信息后显示已修改的文件清单。
git log --name-only
- –name-status
显示新增、修改、删除的文件清单。
git log --name-status
- –abbrev-commit
仅显示 SHA-1 的前几个字符(默认显示七个字符,不过有时为了避免 SHA-1 的歧义,会增加字符数),而非所有的 40 个字符
git log --abbrev-commit
- –graph
显示 ASCII 图形表示的分支合并历史。
git log --graph
- –pretty
使用其他格式显示历史提交信息。可用的选项包括 oneline,short,full,fuller 和 format(后跟指定格式)。
git log --pretty=
如用 format,可以定制要显示的记录格式(format 常用的选项):
$ git log --pretty=format:"%h - %an, %ar : %s"
ca82a6d - Scott Chacon, 6 years ago : changed the version number
085bb3b - Scott Chacon, 6 years ago : removed unnecessary test
a11bef0 - Scott Chacon, 6 years ago : first commit
- –since, –after
仅显示指定时间之后的提交(since和after都可以)
git log --since=2.weeks #两周前的提交 这里用=或空格都可以
- –until, –before
仅显示指定时间之前的提交(until和before都可以)
git log --before 2016-10-01
- –author
仅显示指定作者相关的提交。(作者指的是实际作出修改的人)
git log --author [stone]
- –committer
仅显示指定提交者相关的提交。(提交者指的是最后将此工作成果提交到仓库的人)
git log --committer [zsan]
- –grep
仅显示含指定关键字的提交
git log --oneline --grep "add"
如,输出:
- -g选项
这个命令会以标准日志的格式输出引用日志
git log -g #后可跟分支名,若不跟,表示当前分支
- 提交区间
提交区间来解决 “这个分支还有哪些提交尚未合并到主分支?” 的问题。
1.双点
如,查看newb分支还有哪些提交未合并到master分支:
git log master..newb#未被master包含的newb提交
如,查看当前分支即将推送到origin/master的内容:
git log origin/master..HEAD #HEAD可以留空,默认当前分支最新就是HEAD
2.多点
Git 允许你在任意引用前加上 ^ 字符或者 –not 来指明你不希望提交被包含其中的分支。因此下列3个命令是等价的:
git log refA..refB
git log ^master newb#查询不包含在master中的,newb提交信息
git log newb --not master
这个语法很好用,因为你可以在查询中指定超过两个的引用,这是双点语法无法实现的。 比如,你想查看所有被 master 或 newb 包含的但是不被 newc 包含的提交,你可以输入下面中的任意一个命令:
git log master newb ^newc
git log master newb --not newc
3.三点
查询两个引用中,被一个包含,但又不都包含的提交。
git log newb...newc
注:当要退出log视图,在冒号后,按q即可
查看当前分支的commit记录。在reset 到历史版本后,git log命令是看不到新的提交记录的
查看分支引用
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
如,你想要查看一个分支的最后一次提交的对象,假设 topic1 分支指向 ca82a6d ,那么以下的命令是等价的:
git show ca82a6dff817ec66f44342007202690a93763949
git show topic1
如,想要查看分支topic1,对应的完整SHA-1值:
git rev-parse topic1
或,想要查看某个简写SHA-1值对应的完整值:
git rev-parse ca82a6d
注:在使用简写SHA-1的commit引用时,最低4位
reflog 引用日志
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- 当你正在工作时,Git 会默默地记录每一次你改变 HEAD 时它的值。 每一次你提交或改变分支,引用日志都会被更新
git reflog
类似输出:
734713b [email protected]{0}: commit: fixed refs handling, added gc auto, updated
d921970 [email protected]{1}: merge phedders/rdocs: Merge made by recursive.
1c002dd [email protected]{2}: commit: added some blame and merge stuff
1c36188 [email protected]{3}: rebase -i (squash): updating HEAD
95df984 [email protected]{4}: commit: # This is a combination of two commits.
1c36188 [email protected]{5}: rebase -i (squash): updating HEAD
7e05da5 [email protected]{6}: rebase -i (pick): updating HEAD
每当你的 HEAD 所指向的位置发生了变化,Git 就会将这个信息存储到引用日志这个历史记录里。 通过这些数据,你可以很方便地获取之前的提交历史。
- 如果你想查看仓库中 HEAD 在五次前的所指向的提交,你可以使用 @{n} 来引用 reflog 中输出的提交记录:
git show HEAD@{5}
git show master@{3} #查看master分支在前三次的的提交
git show master@{yesterday}#查看昨天的
git show [email protected]{2.months.ago}#查看2月前引用日志。
#其它可用的时间值:years, months, days, hours, minutes, seconds
- 可以运行 git log -g 来查看类似于 git log 输出格式的引用日志信息
git log -g master
类似输出:
commit 734713bc047d87bf7eac9674765ae793478c50d3
Reflog: [email protected]{0} (Scott Chacon <[email protected].com>)
Reflog message: commit: fixed refs handling, added gc auto, updated
Author: Scott Chacon <[email protected].com>
Date: Fri Jan 2 18:32:33 2009 -0800
fixed refs handling, added gc auto, updated tests
- 可以使用 HEAD^ 来查看上一个提交,也就是 “HEAD 的父提交”。
这个语法只适用于合并(merge)的提交,因为合并提交会有多个父提交。 第一父提交是你合并时所在分支,而第二父提交是你所合并的分支。
如下的例子,当前分支提交了6b43d14、467e8c1,合并其他分支的提交2aef572,合并后的提交为6f12bbc。
通过git log --pretty=format:‘%h %s‘ --graph
查看log :
* 6f12bbc 合并了
|\
| * 2aef572 CC
* | 467e8c1 AA
* | 6b43d14 write newbbb
|/
* 6772a20 newccc
* d1a41a8 newc
* db4f9bb newm
* 6af9641 2update a
* 0211e59 merge a
|\
| * 9024fe6 update a
* | 317888e modifyd a
|/
* 1912753 update a
* c7fcd00 upupup:
* 077d7ad add aa
* 2a518c5 aaffff
输入命令git show HEAD^
,则会输出第一父提交467e8c1的提交信息
输入命令git show HEAD^2
,则会输出第二父提交2aef572的提交信息
- 可以使用 HEAD~ 来查看上一个提交。
git show HEAD^
和git show HEAD~
作用是等价的。但后面跟上数字的作用就不一样了。~ 只算当前分支的父提交
git show 6f12bbc~2 #输出6b43d14引用
git show 6f12bbc~8 #输出317888e引用
git show 6f12bbc~7^2 #当前第7父提交的第2父,即输出9024fe6
merge 合并分支
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- 列出是否合并到当前分支的分支
1.已经合并到当前分支的分支
git branch --merged
2.尚未合并到当前分支的分支
git branch --no-merged
- 分支合并
要记住的一点是,以当前分支,来merge其他分支。
git merge [otherBranch]#当前分支合并otherBranch分支
Git 会自行决定选取哪一个提交作为最优的共同祖先,并以此作为合并的基础。
- 分支合并引发冲突
有时候合并操作不会如此顺利。 如果你在两个不同的分支中,对同一个文件的同一个部分进行了不同的修改,Git 就没法干净的合并它们,那就会引发冲突conflict。
例如,在master分支和newb分支,都修改了a文件,并提交了。现在想以master来合并newb分支。
$ git merge newb
Auto-merging a
CONFLICT (content): Merge conflict in a
Automatic merge failed; fix conflicts and then commit the result.
提示说a文件有冲突。
当有冲突后,通过git status
命令,看到:
On branch master
You have unmerged paths.
(fix conflicts and run "git commit")
(use "git merge --abort" to abort the merge)
Unmerged paths:
(use "git add <file>..." to mark resolution)
both modified: a
现在打开a文件,看到:
<<<<<<< HEAD
aawww
=======
adminwww
>>>>>>> newb
<<
HEAD和==间的就是当前master分支上的内容;==和>>
间的就是newb分支上的内容。
现在,只需手动清除不需要的内容(含这些说明字符)后,再add、commit即可。
- 合并工具
如果你想使用图形化工具来解决冲突,可以运行
git mergetool
,该命令会为你启动一个合适的可视化合并工具,并带领你一步一步解决这些冲突。如果没有打开工具,根据提示,输入
git mergetool --tool-help
,会列出本地已支持的工具列表,和支持的其他工具列表(需要自行安装)。比如,我这里本地支持opendiff工具,然后需要配置一下:
git config --local merge.tool opendiff
rebase 变基合并分支
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
变基的大意就是指,指定基分支(紧跟在rebase后),然后与指定分支分并。这时git会查询出两个分支共同的祖先,以基分支为基础,将要合并分支的commit取消掉,在 .git/rebase-apply/ 下生成一个patch文件。然后自动apply。(这里说的有点绕,看下面的命令后的注释,比较好理解)
#合并newb分支,newb为基,当前分支的新commit续到newb后,最终影响当前分支
git rebase [newb]
#newb分支为基,master中的变更,被续到newb后,最终影响master分支
#这样跟先切换到master分支,再使用上面的reabse命令,效果一样
git rebase [newb master]
变基使得提交历史更加整洁。你在查看一个经过变基的分支的历史记录时会发现,尽管实际的开发工作是并行的,但它们看上去就像是串行的一样,提交历史是一条直线没有分叉。通过git log --graph
图标式查看log,就能看到有些commit有分叉,那就是使用的merge方式进行合并了。
- rebase引发冲突
当解决了一个冲突,需要用”git-add”命令去更新这些内容的索引(index),然后使用如下命令,继续应用下一个补丁。当所有冲突都解决了,也使用它,从而完成合并。
git rebase --continue
当想跳过patch,即只想要rebase分支内容,可用如下命令:
git rebase --skip
中断rebase合并操作:
git rebase --abort
- –onto选项
这个是应用在有三个分支的合并场景的。
如:
git rebase --onto [master server client]
表示:以master为基,找出server、client在master上的共同祖先之后的修改,只将符合条件的client修改进行应用。最后被修改的分支为client,server只是起到一个组合查询的作用。
变基的风险:不要对在你的仓库外有副本的分支执行变基。
如,你push了c3、c4、c5三个提交;然后被其他同事pull应用后;
你又使用rebase模式,合并了一个分支newb,若它们的共同祖先是c2,则c3、c4、c5就会先取消,后被重新应用并commit到newb分支的末尾。接着你再push,那么c3、c4、c5又会push一次。
这时,其他人若要pull,要使用git pull –rebase
只要你把变基命令当作是在推送前清理提交使之整洁的工具,并且只在从未推送至共用仓库的提交上执行变基命令,就不会有事
其它参见 Git-工具-重写历史。里面有有意思的选项为:filter-branch
bisect 二分查找记录
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
比如有一个bug,不知道哪个历史提交引起的。这时就可以用bisect相关命令来查找。该命令采用二分法查找
- 开始查找:
git bisect start
使用git log,能看到提交记录(如要用到,可以只取前7位),
通过从提交记录检出成新分支或git reset方式,再运行调试,查看bug是否存在。
最好是重新拉一个新的项目仓库下来,专门来做这个调试工作。
- 当确认bug存在时,标记找到问题:
git bisect good 33f4b9e #在33f4b9e提交记录中,有问题
- 接着再找一个相对较早一些的提交记录(自己评估下),运行、调试。
若存在bug,操作同上。若不存在,标记未找到问题:
git bisect bad 0a94086 #在0a94086提交记录中,未找到
- 当至少有一个bad和一个good后,就会提示,大概还需要多少次就能定位到问题了:
Bisecting: 12 revisions left to test after this (roughly 4 steps)
大意:中间还有12个版本,大概最多还要4次测试,就能定位到问题了。
注:文末引用文章中,把没有问题的标记为good,有问题的才标记为bad。
这里的示例,是将有问题的标记为good,没有问题的标记为bad。
blame 责任查找
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
这个命令可以将文件中的每一行的作者、最新的变更提交和提交时间展示出来。
git blame [file_name]
diff 比较文件改动差异
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- 比较未暂存文件改动差异
比如一个已add的文件,经过改动后,尚未再次add,输入命令:
git diff
如图:比较SpacePresenter 最新版本记录(-)和当前改动(+)后,文件间的差异
- 比较已暂存文件改动差异
git diff --cached
git diff --staged #功能同cached。 git1.6.1及更高版可用
比较文件的前后差异。可以通过GUI工具来查看,show difference
Git Diff 的插件版本
可以使用 git difftool 命令来用 Araxis ,emerge 或 vimdiff 等软件输出 diff 分析结果。 使用 git difftool –tool-help 命令来看你的系统支持哪些 Git Diff 插件。 如需要配置请使用
git config --global diff.tool Araxis
patch 补丁
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
如果项目,只有你一个人在维护。其他人想要commit、push是不行的。但他们clone下来项目,经过修改后,可以生成并发送一个patch给你,让你来合并代码。
用到的命令:
git format-patch #生成patch文件
git apply xxx.patch #应用patch
注:由于没有完全试验过,这里说的比较简略
remote 远程仓库
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
如果是从远程仓库检出的项目,可以直接输入命令:
git remote
至少能看到类似 origin 的输出。origin是一般默认的远程仓库名。
- -v选项
会显示需要读写远程仓库使用的 Git 保存的简写与其对应的 URL。
git remote -v
如,输出:
origin https://github.com/googlesamples/android-architecture.git (fetch)
origin https://github.com/googlesamples/android-architecture.git (push)
fetch,是客户端拉取最新信息的地址;push就是上传的地址。
- add选项
添加一个新的远程 Git 仓库,同时指定一个你可以轻松引用的简写:
git remote add [pb https://github.com/paulboone/ticgit]
如上,pb就是指定的远程仓库简写。
再用-v命令查看:
$ git remote -v
origin https://github.com/schacon/ticgit (fetch)
origin https://github.com/schacon/ticgit (push)
pb https://github.com/paulboone/ticgit (fetch)
pb https://github.com/paulboone/ticgit (push)
现在,就关联了两个远程仓库地址了。在fetch或push时,可以指定远程仓库名,如:
git push [pb master] # push到远程仓库pb上的master分支
git fetch [pb] # fetch远程仓库pb上所有新的信息
- show选项
git remote show [origin]
会输出fetch、push的url,当前所在分支,不在本地的远程分支,哪些远程分支已经从服务器上移除了,还有pull和push时本地与远程分支的关联
- rename选项
重命名引用的远程仓库名字
git remote rename [pb paul]
- rm选项
删除远程仓库引用。
git remote rm [paul] #rm 换成 remove也是可以的
clean 清理工作目录
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
你需要谨慎地使用这个命令,因为它被设计为从工作目录中移除未被追踪的文件。 如果你改变主意了,你也不一定能找回来那些文件的内容。 一个更安全的选项是运行 git stash --all
来移除每一样东西并存放在栈中。
可以使用git clean
命令去除冗余文件或者清理工作目录。 使用git clean -f -d
命令来移除工作目录中所有未追踪的文件以及空的子目录。 -f 意味着 强制 或 “确定移除”。
如果只是想要看看它会做什么,可以使用 -n 选项来运行命令,这意味着 “做一次演习然后告诉你 将要 移除什么”。
git clean -d -n
输出:
Would remove test.o
Would remove tmp/
默认情况下,git clean 命令只会移除没有忽略的未跟踪文件。 任何与 .gitiignore 或其他忽略文件中的模式匹配的文件都不会被移除。 如果你也想要移除那些文件,例如为了做一次完全干净的构建而移除所有由构建生成的 .o 文件,可以给 clean 命令增加一个 -x 选项。
git clean -n -d -x
如果不知道 git clean 命令将会做什么,在将 -n 改为 -f 来真正做之前,总是先用 -n 来运行它做双重检查。
Git 打包
使用GitHub上的开源项目
当clone好项目后,可以建立自己的本地分支,进行调试等。
这样方便,以后合并项目某个分支,主分支单独更新等。
Git 与其他系统
迁移到 Git
Git 验证
用到gpg工具,这个需要单独安装,并配置环境
Reference
https://git-scm.com/book/zh/v2/ 《官方指南》
http://www.cnblogs.com/haiq/archive/2012/12/26/2833746.html 《ignore语法》
http://www.cnblogs.com/wish123/p/3963224.html 《diff命令详解》
http://gitbook.liuhui998.com/5_4.html 《查找问题的利器 - Git Bisect》
http://www.oschina.net/translate/10-tips-git-next-level 《10 个迅速提升你 Git 水平的提示》
http://www.ruanyifeng.com/blog/2014/06/git_remote.html 《Git远程操作详解》