序言
Git基本概念
svn的提交是一步提交,即将本地代码一步commit到中央服务器,但是git跟这个概念完全不同,git有工作区、暂存区、本地版本库、远程版本库的概念。
- 工作区:你的workspace下的工程名文件夹
- 版本库:工作区的隐藏目录.git
- stage(index):暂存区
- master:git自动创建的第一个分支,指向最新提交
- HEAD:指向master的一个指针
这个图非常重要,一定要理解。
git的要达到类似svn的提交程度需要进行三步:
- 第一步【git add】将文件修改添加到暂存区stage
- 第二步【git commit】提交更改,将暂存区的所有内容提交到本地版本库
- 第三步【git push】将当前分支的本地版本库信息推送到远程版本库
Git基本命令
1、初始设置
git使用的第一步必须先设定自己的用户名、邮箱地址
1 | 当前用户所有项目默认使用 |
2、设置别名
1 | 设置别名 git st = git status |
说明:
Git 提供了一个叫做 git config 的工具专门用来配置或读取相应的工作环境变量。而正是由这些环境变量,决定了 Git 在各个环节的具体工作方式和行为。这些变量可以存放在以下三个不同的地方:
- /etc/gitconfig 文件:系统中对所有用户都普遍适用的配置。若使用 git config 时用**–system** 选项,读写的就是这个文件。
- ~/.gitconfig 文件:用户目录下的配置文件只适用于该用户。若使用 git config 时用**–global** 选项,读写的就是这个文件。
- 当前项目的 git 目录中的配置文件(也就是工作目录中的 .git/config 文件):这里的配置仅仅针对当前项目有效。每一个级别的配置都会覆盖上层的相同配置,所以.git/config 里的配置会覆盖/etc/gitconfig 中的同名变量。
在 Windows 系统上,Git 会找寻用户主目录下的 .gitconfig 文件。主目录即 $HOME 变量指定的目录,一般都是C:\Documents and Settings$USER。此外,Git 还会尝试找寻/etc/gitconfig 文件,只不过看当初 Git 装在什么目录,就以此作为根目录来定位。
3、clone远程git仓库到本地
1 | git协议clone |
常见场景
github本地免密码
1)、本地生成SSH KEY
1 ssh-keygen -t rsa -C "mail@xxx.net"上面命令一路回车会生成如下3个文件
- id_rsa :私钥
- id_rsa.pub :公钥,需要保存至GitHub帐号中
- known_hosts
2)、github.com上点击Personal settings -》SSH keys-》Add SSH key添加id_rsa.pub文件的内容
3)、验证是否成功
1
2
3 ssh -T git@github.com
Hi XXXX! You've successfully authenticated, but GitHub does not provide shell access.
常见clone错误解决方法
1 | git clone http://xxxx.com/xxx.git |
4、分支命令
4.1 基本命令
1 | 查看本地分支列表,前带*号的为当前分支 |
4.2 高频使用分支命令
1 | 新建dev分支并与远程分支建立关联,且切换至该分支 |
4.3 谨慎使用分支命令
1 | 删除本地分支 |
4.4 更新所有分支
1 | git fetch --all |
4.5 Git分支切换要点
官方文档推荐在分支切换之前保证工作区clean,这个原则很重要原因如下:
1、新增的文件
分支切换会带着这个文件到新分支中。新增的文件在git中是个特殊的存在,因为并未被git管理起来
2、更新的文件
三种情况:
- 被更新的文件在新分支中没有,不能切换;
- 被更新的文件在新分支中有,且跟当前分支的父对象不同(内容相同也不可以) ,不能切换分支;
- 被更新的文件在新分支中有,且跟当前分支有共同父对象,这个文件内容会带到新分支中;
情况多样且复杂,由此看来分支切换之前保证工作区clean非常有必要!
当然此时也可以用 git stash命令来暂存工作区和暂存区的文件
1 | git stash |
(此处注意两点:新添加的文件不能暂存;每次执行此命令,最新的永远是**stash@{0}**)
5、代码提交过程
1 | 1、修改文件 |
6、git reset
6.1 unstage
git reset [HEAD] filename
举例说明
1 | unstage基本用法 |
6.2 退回本地仓库提交
git reset –[soft | mixed | hard ] 提交
–soft:1、提交退回暂存区stage(index)
–mixed:2、提交提交退回工作区(参数可以省略)
–hard:3、丢弃commit
1 | 具体验证过程 |
注意:已经push到远程仓库的commit不允许reset
7、已提交未推送修改commit的注释信息
1 | amend:[əˈmend] 修订; 改良,修改; |
1 | 撤销所有未提交的内容(将导致工作区内容全部丢失) |
8、按行追责
Git blame用于查看文件中每行的commit哈希标签、作者和提交日期。定位错误代码的修改人
1 | blame v.把…归咎于; 责怪; 指责;n.(坏事或错事的) 责任; 责备; 指责; |
9、git bisect 快速定位 bug
1 | 开始定位 |
Git tag
对某一时间点的版本打上标签。
1 | 列出已有标签 |
Git log
1 | 一行显示日志信息,排除包含Merge branch字符串的描述,导出到lic.txt文件中 |
Git diff
1 | 获取git最近3次提交修改的文件列表 |
Git 储藏
git stash命令用于本地暂时存储对工作区的未提交的修改,当暂时不方便提交,又有紧急BUG需要切换分支时最常用,也可以将修改存储后,恢复到其他需要的分支。
1 | 一、基本操作 |
git apply
1 | 比对工作区与暂存区差别,且导出到patch文件 |
更改仓库地址
1 | git remote set-url origin [new-url] |
.gitignore配置
可以在目录下增加这个文件来配置忽略不提交的文件,当前目录以及子目录都起作用,当然也可以设置强制某些git默认不管理的文件,进行管理。如exe文件,另外不必自己从头配置这个文件,官网提供了各种语言、项目的.gitignore模板,官网gitignore模板下载一份相关的改一下即可。
自定义配置举例
1 | My configurations: |
.gitignore常见问题
把已经提交至git仓库的文件,添加.gitignore不起作用,因为此时想忽略的文件已经被git追踪管理起来了。若想忽略已经提交的文件可以采用如下方式
1 | 更新.gitignore文件,添加要忽略的文件或文件夹,后执行下面的命令 |
git分支与tag重名
比较麻烦的是每个人都将tag同步到了本地,你即使删除掉了远程的同名tag,依然会有人推送本地所有tag到远程(如:sourcetree默认推送会勾选【】)
查看分支的创建时间
git reflog show –date=iso bug_on_staging
1 | 删除服务器tag |
高级操作
官方高级操作说明文档里面列举了6种问题的解决方案,可以去了解一下。如下是我自己实践过程中总结的相比较而言比较高级的操作,希望对你有用
批量删除
1 | 批量删除master or dev分支以外的所有分支 |
git分支合并忽略部分文件
合并分支时忽略某些文件,举例说明两个分支 base、基于base创建target
正常的流程是
1 | 1、base分支新建.gitattributes文件 |
结论:
合并分支要忽略的文件必须分别做过commit,此时合并分支才会忽略
这样看来ours应该这样理解,我有自己的提交了,之后都以我自己的为准。
git移库
想带着历史所有信息迁移git仓库,可以采用如下方式:
demo.git -> target.git
1 | linux新建裸库 |
验证是否迁移成功
1 | 克隆目标库 |
可能出现的问题
1、问:新建的裸库,可以克隆但是不能推送
答:检查裸库目录的权限的拥有者和群组
ls -al 或者 ll命令
1 | ls -al |
此时本地仓库是没法推送的
1 | git clone git@XXX.com/xxx/target.git |
需要将拥有者和群组设置成跟地址git@XX前缀一致的
1 | chown -R git:git target.git |
2、若两个仓库之间有一样的文件,移库是有问题的,此时拉取会报错。
1 | git pull |
推荐:新建的裸库尽量一点东西没有,特别不要有跟要迁移仓库一样的文件
git修改错误的作者信息
现实中,经常会切换不同的git仓库,默认git config –global user.name|user.email全局配置肯定不能满足你公司跟自有仓库的身份切换,时常一不小心身份信息提交错了,且推送到远程了,此时可以用如下方案进行修改。
1、打开git bash
2、bare clone你的仓库
1 | git clone --bare https://github.com/user/repo.git |
3、修改如下命名的OLD_EMAIL
,CORRECT_NAME
,CORRECT_EMAIL
1 | git filter-branch --env-filter ' |
4、粘贴修改后的3,回车执行
5、推送到远程
1 | git push --force --tags origin 'refs/heads/*' |
6、本地删除刚刚clone的仓库
1 | cd .. |
提示:除非必要,否则不建议如此操作
参考资料:
https://github.com/cytopia/git-rewrite-author
https://help.github.com/articles/changing-author-info/#platform-windows
https://www.everythingcli.org/git-like-a-pro-rewrite-author-history/
git clone的三种类型
git clone有三种类型,分别是默认的 non-bare, bare 和 mirror。那么这三者之间到底有什么区别?
The difference is that all refs are copied as-is. This means everything: remote-tracking branches, notes, refs/originals/* (backups from filter-branch). The cloned repo has it all. It’s also set up so that a remote update will re-fetch everything from the origin (overwriting the copied refs). The idea is really to mirror the repository, to have a total copy, so that you could for example host your central repo in multiple places, or back it up. Think of just straight-up copying the repo, except in a much more elegant git way.
The new documentation pretty much says all this:
--mirror
Set up a mirror of the source repository. This implies
--bare
. Compared to--bare
,--mirror
not only maps local branches of the source to local branches of the target, it maps all refs (including remote branches, notes etc.) and sets up a refspec configuration such that all these refs are overwritten by agit remote update
in the target repository.
My original answer also noted the differences between a bare clone and a normal (non-bare) clone - the non-bare clone sets up remote tracking branches, only creating a local branch forHEAD
, while the bare clone copies the branches directly.
Suppose origin has a few branches (master (HEAD)
, next
, pu
, and maint
), some tags (v1
,v2
, v3
), some remote branches (devA/master
, devB/master
), and some other refs (refs/foo/bar
, refs/foo/baz
, which might be notes, stashes, other devs’ namespaces, who knows).
git clone origin-url
(non-bare): you will get all of the tags copied, a local branchmaster (HEAD)
tracking a remote branchorigin/master
, and remote branchesorigin/next
,origin/pu
, andorigin/maint
. The tracking branches are set up so that if you do something likegit fetch origin
, they’ll be fetched as you expect. Any remote branches (in the cloned remote) and other refs are completely ignored.git clone --bare origin-url
: you will get all of the tags copied, local branchesmaster (HEAD)
,next
,pu
, andmaint
, no remote tracking branches. That is, all branches are copied as is, and it’s set up completely independent, with no expectation of fetching again. Any remote branches (in the cloned remote) and other refs are completely ignored.git clone --mirror origin-url
: every last one of those refs will be copied as-is. You’ll get all the tags, local branchesmaster (HEAD)
,next
,pu
, andmaint
, remote branchesdevA/master
anddevB/master
, other refsrefs/foo/bar
andrefs/foo/baz
. Everything is exactly as it was in the cloned remote. Remote tracking is set up so that if you rungit remote update
all refs will be overwritten from origin, as if you’d just deleted the mirror and recloned it. As the docs originally said, it’s a mirror. It’s supposed to be a functionally identical copy, interchangeable with the original.
git 统计
统计提交次数
1 | git shortlog -s -n |
统计某段时间某人提交情况
1 | git log --format='%aN' --since=2017-01-01 --until=2017-03-31 --all | sort -u | while read name; do echo -en "$name\t"; git log --author="$name" --pretty=tformat: --numstat | awk '{ add += $1; subs += $2; loc += $1 - $2 } END { printf "added lines: %s, removed lines: %s, total lines: %s\n", add, subs, loc }' -; done |
异常问题解决
1、更新、拉取报错
schannel: next InitialzeSecurityContext failed: Unknown error (0x80092013)
1 | git config --global http.schannelCheckRevoke false |
问题原因
GitHub Desktop by default uses the Windows Secure Channel (SChannel) APIs to validate the certificate received from a server. Some networks will block the attempts by Windows to check the revocation status of a certificate, which then causes the whole operation to error.
附录
背景知识
获取git 命令帮助
1 | 显示基本命令参数 |
git 的HEAD移动(~和^区别)
head:默认指向最后那次commit = head^0 ,位置不固定,可以使用git checkout切换指向不同的commit
head^:head提交的第1个父提交
head^2:head提交的第2个父提交
head^3:head提交的第3个父提交
head~1:head提交的父提交
head~2:head父提交的父提交 = head^^ = head^1^1
head~2^2:head父提交的父提交的第二个提交
1 | git lg |
~和^区别总结:
~
:竖向找其父节点(多个父节点的话其第一个)
->爸爸(head~1
)
->爷爷(head~2
)
->老爷爷(head~3
)
^
:横向找其第N个父节点
爸爸层横着查找(父级节点横向定位)
->爸爸(老大)(head~1
)
->二叔(head^2
)
->三叔 (head^3
)
爷爷层横着查找
->爷爷(老大)(head~2
)
->二爷爷(head~2^2
)
常见的用法
head^0 = head (head~0 = head)
head^1 = head^ = head~ = head~1
head~2 = head^^ = head^1^1 != head^2(注意此处不想等)
head~3 = head^^^ = head^1^1^1
git命令行参数 - 和–的区别
1
2
3
4
5 linux下
1、ls -a (参数前一横)
2、cp --help (参数前两横)
3、tar -xzvf (参数前一横)
4、tar xzvf (参数前没有横)解释:
第一种:参数用一横的说明后面的参数是字符形式。
第二种:参数用两横的说明后面的参数是单词形式。
第三种:参数前有横的是 System V风格。
第四种:参数前没有横的是 BSD风格。System V和BSD的区别(稍作了解就行):
System V 和BSD同出于AT&T实验室的两个不同的部门
SystemV是一个Unix的商业化标准;
- RedHat、Suse、MDV、MagicLinux、Debian
- AIX、IRIX、Solars、HP-UX
BSD为Unix标准化的Berkeley风格(Slackware:Linux发行版中唯一使用BSD风格的版本)。
- FreeBSD
- NetBSD
- OpenBSD
系统启动过程中 kernel 最后一步调用的是 init 程序,init 程序的执行有两种风格,即 System V 和 BSD。它们的目的相同,都是根据 runlevel 执行一系列的程序。
- System V 风格中 init 调用 /etc/inittab
- BSD 风格调用 /etc/rc
入门参考
TODO
- git merge 和 git rebase
- 搞清楚git rm操作
- git bisect 快速定位 bug
修订记录
2019-05-06 17:01 完善log,增加删除文件恢复方法;完善HEAD移动
2019-02-28 17:36 完善git stash、git config
2019-02-26 16:03 完善基础命令,整理提交过程;完善附录,解决图片服务器失效问题
2017-05-24 20:56 新增.gitignore配置