Git 复习重新开始
- 全局配置
$ git config --global user.name "Your Name"
$ git config --global user.email "email@example.com"
- 初始化
git init
- 添加到暂存区
//git add a.txt
git add .
- 添加到本地的版本库
git commit -m 'des'
- 查看当前的状态
git status
- 打印commit记录
git log
//git log --pretty=oneline
- 回退版本
git reset --hard HEAD^
git reset --hard HEAD^^
git reset --hard 'commit id'
- 查看所有记录
git reflog
- 撤销修改
场景1:当你改乱了工作区某个文件的内容,想直接丢弃工作区的修改时,用命令git checkout -- file
。
场景2:当你不但改乱了工作区某个文件的内容,还添加到了暂存区时,想丢弃修改,分两步,第一步用命令git reset HEAD file
,就回到了场景1,第二步按场景1操作。
场景3:已经提交了不合适的修改到版本库时,想要撤销本次提交,参考版本回退一节,不过前提是没有推送到远程库
- 删除文件
rm a.txt
git status
// 1 确认要删除
git rm a.txt
git commit -m 'des'
// 2删错,想要回退
$ git checkout -- test.txt
git checkout
其实是用版本库里的版本替换工作区的版本,无论工作区是修改还是删除,都可以“一键还原”。
命令git rm
用于删除一个文件。如果一个文件已经被提交到版本库,那么你永远不用担心误删,但是要小心,你只能恢复文件到最新版本,你会丢失最近一次提交后你修改的内容。
- 创建
ssh key
先查看用户主目录下有没有.ssh
如果有进入,查看id_rsa
(私钥),id_rsa.pub
(公钥),使用的事公钥,
cat id_rsa.pub
如果没有.ssh,则需要创建
$ ssh-keygen -t rsa -C "youremail@example.com"
- 添加远程仓库
$ git remote add origin git@github.com:<your name>/learngit.git
$ git push -u origin master
Counting objects: 19, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (19/19), done.
Writing objects: 100% (19/19), 13.73 KiB, done.
Total 23 (delta 6), reused 0 (delta 0)
To git@github.com:michaelliao/learngit.git
* [new branch] master -> master
Branch master set up to track remote branch master from origin.
由于远程库是空的,我们第一次推送master分支时,加上了-u参数,Git不但会把本地的master分支内容推送的远程新的master分支,还会把本地的master分支和远程的master分支关联起来,在以后的推送或者拉取时就可以简化命令。
$ git push origin master
- 从远端克隆
$ git clone
- 创建与合并分支
// 创建并且切换分支到dev
git checkout -b dev
// 等同于
git branch dev
git checkout dev
git status
*dev
master
git add .
git commit -m 'des'
git checkout master
git merge dev
git checkout dev
// 删除dev分支
git branch -d dev
这次合并是“快进模式”,也就是直接把master指向dev的当前提交,所以合并速度非常快。
- 解决冲突
git branch -b feature1
修改read.txt
git add .
git commit -m 'and'
git checkout master
在master read.txt也修改
git add .
git commit -m 'AND'
git merge dev
就会产生冲突
git status
<<<<<<< HEAD
Creating a new branch is quick AND simple.
=======
Creating a new branch is quick and simple.
>>>>>>> feature1
然后保留一个修改,删除另一个
git add .
git commit -m 'merge'
// 查看合并情况
git log --graph --pretty=oneline --abbrev-commit
// 删除分支feature1
git branch -d feature
- 分支管理策略
通常,合并分支时,如果可能,Git会用Fast forward模式,但这种模式下,删除分支后,会丢掉分支信息。
如果要强制禁用Fast forward
模式,Git就会在merge时生成一个新的commit,这样,从分支历史上就可以看出分支信息。
下面我们实战一下--no-ff
方式的git merge:
首先,仍然创建并切换dev分支
git branch -b dev
// 修改一些文件
git add .
git commit -m 'done'
git checkout master
使用 --no-ff
合并分支
git merger --no-ff -m '--no-ff merger test' dev
// 查看合并的状态
$ git log --graph --pretty=oneline --abbrev-commit
* 7825a50 --no-ff merger test
|\
| * 6224937 done
|/
* 59bc1cb conflict fixed
...
- bug分支
在dev分支开发是,工作到一半的时候,可能还需要一定时间才能完成(还未提交),但是此时此需要一个小时来解决一个Bug
可以是使用
git stash
把现在的工作现场保存起来在使用git status
就会发现工作区是clear的
然后选定在哪个分支上进行修改bug,这里假设是在master分支上修改的
git checkout master
git branch -b issue101
// 假设已经修改了一些代码
git add .
git commit -m 'done'
// 合并
git checkout master
git merge --no-ff -m 'issue-101 fixed' issue101
此时bug修改完毕,切换到dev分支
git checkout dev
git status
会发现dev分支的工作区是clear的
git stash list
stash@{0}: WIP on dev: 6224937 add merge
工作现场还在,Git把stash内容存在某个地方了,但是需要恢复一下,有两个办法:
一是用git stash apply
恢复,但是恢复后,stash内容并不删除,你需要用git stash drop
来删除;
另一种方式是用git stash pop
,恢复的同时把stash内容也删了:
git stash pop
git stash list
//会发现是空的
你可以多次stash,恢复的时候,先用git stash list查看,然后恢复指定的stash,用命令:
$ git stash apply stash@{0}
- feature分支
新的功能
git checkout -b feature2
一段时间后开发完成
git add .
git commit -m 'feature'
// 切回到dev分支
git checkout dev
由于种种原因 决定放弃这个功能(但是此时还没合并分支)
git branch -d feature2
error: The branch 'feature2' is not fully merged.
If you are sure you want to delete it, run 'git branch -D feature2'.
$ git branch -D feature1
Deleted branch feature1 (was 756d4af).
- 多人协作
查看远程分支
git remote
origin
或者
git remote -v
origin git@github.com:<name>/<project-name>.git (fetch)
origin git@github.com:<name>/<project-name>.git (push)
上面显示了可以抓取和推送的origin的地址。如果没有推送权限,就看不到push的地址。
A小伙伴:
git clone ......
克隆下来的项目只能看见master分支
git branch
* master
现在,你的小伙伴要在dev分支上开发,就必须创建远程origin的dev分支到本地,于是他用这个命令创建本地dev分支:
git checkout -b dev origin/dev
A小伙伴就可在dev分支上进行修改等等,比如
git add .
git commit -m ''
git push origin master
就在此时,我要对同一个地方进行了修改,并且要提交
git add hello.py
git commit -m 'add python'
git push origin dev
会发现推送失败了;因为你的小伙伴的最新提交和你试图推送的提交有冲突,解决办法也很简单,Git已经提示我们,先用git pull把最新的提交从origin/dev抓下来,然后,在本地合并,解决冲突,再推送:
git pull
remote: Counting objects: 5, done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 3 (delta 0)
Unpacking objects: 100% (3/3), done.
From github.com:michaelliao/learngit
fc38031..291bea8 dev -> origin/dev
There is no tracking information for the current branch.
Please specify which branch you want to merge with.
See git-pull(1) for details
git pull <remote> <branch>
If you wish to set tracking information for this branch you can do so with:
git branch --set-upstream dev origin/<branch>
还是失败了,原因是没有指定本地dev分支与远程origin/dev分支的链接,根据提示,设置dev和origin/dev的链接
$ git pull
Auto-merging hello.py
CONFLICT (content): Merge conflict in hello.py
Automatic merge failed; fix conflicts and then commit the result.
这回git pull成功,但是合并有冲突,需要手动解决,解决的方法和分支管理中的解决冲突完全一样。解决后,提交,再push:
$ git commit -m "merge & fix hello.py"
[dev adca45d] merge & fix hello.py
$ git push origin dev
Counting objects: 10, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (5/5), done.
Writing objects: 100% (6/6), 747 bytes, done.
Total 6 (delta 0), reused 0 (delta 0)
To git@github.com:michaelliao/learngit.git
291bea8..adca45d dev -> dev
- 忽略文件
有些时候,你必须把某些文件放到Git工作目录中,但又不能提交它们,比如保存了数据库密码的配置文件啦,等等,每次git status都会显示Untracked files ...,有强迫症的童鞋心里肯定不爽。
好在Git考虑到了大家的感受,这个问题解决起来也很简单,在Git工作区的根目录下创建一个特殊的.gitignore文件,然后把要忽略的文件名填进去,Git就会自动忽略这些文件。
不需要从头写.gitignore文件,GitHub已经为我们准备了各种配置文件,只需要组合一下就可以使用了。所有配置文件可以直接在线浏览:https://github.com/github/gitignore
忽略文件的原则是:
忽略操作系统自动生成的文件,比如缩略图等;
忽略编译生成的中间文件、可执行文件等,也就是如果一个文件是通过另一个文件自动生成的,那自动生成的文件就没必要放进版本库,比如Java编译产生的.class文件;
忽略你自己的带有敏感信息的配置文件,比如存放口令的配置文件。
举个例子:
假设你在Windows下进行Python开发,Windows会自动在有图片的目录下生成隐藏的缩略图文件,如果有自定义目录,目录下就会有Desktop.ini文件,因此你需要忽略Windows自动生成的垃圾文件:
Windows:
Thumbs.db
ehthumbs.db
Desktop.ini
然后,继续忽略Python编译产生的.pyc、.pyo、dist等文件或目录:
Python:
*.py[cod]
*.so
*.egg
*.egg-info
dist
build
加上你自己定义的文件,最终得到一个完整的.gitignore文件,内容如下:
Windows:
Thumbs.db
ehthumbs.db
Desktop.ini
Python:
*.py[cod]
*.so
*.egg
*.egg-info
dist
build
My configurations:
db.ini
deploy_key_rsa
最后一步就是把.gitignore也提交到Git,就完成了!当然检验.gitignore的标准是git status命令是不是说working directory clean。
使用Windows的童鞋注意了,如果你在资源管理器里新建一个.gitignore文件,它会非常弱智地提示你必须输入文件名,但是在文本编辑器里“保存”或者“另存为”就可以把文件保存为.gitignore了。
有些时候,你想添加一个文件到Git,但发现添加不了,原因是这个文件被.gitignore忽略了:
$ git add App.class
The following paths are ignored by one of your .gitignore files:
App.class
Use -f if you really want to add them.
如果你确实想添加该文件,可以用-f强制添加到Git:
$ git add -f App.class
或者你发现,可能是.gitignore写得有问题,需要找出来到底哪个规则写错了,可以用git check-ignore命令检查:
$ git check-ignore -v App.class
.gitignore:3:*.class App.class
Git会告诉我们,.gitignore的第3行规则忽略了该文件,于是我们就可以知道应该修订哪个规则。
小结
忽略某些文件时,需要编写.gitignore;
.gitignore文件本身要放到版本库里,并且可以对.gitignore做版本管理!
- 配置别名
有没有经常敲错命令?比如git status?status这个单词真心不好记。
如果敲git st就表示git status那就简单多了,当然这种偷懒的办法我们是极力赞成的。
我们只需要敲一行命令,告诉Git,以后st就表示status:
$ git config --global alias.st status
好了,现在敲git st看看效果。
当然还有别的命令可以简写,很多人都用co表示checkout,ci表示commit,br表示branch:
$ git config --global alias.co checkout
$ git config --global alias.ci commit
$ git config --global alias.br branch
以后提交就可以简写成:
$ git ci -m "bala bala bala..."
--global参数是全局参数,也就是这些命令在这台电脑的所有Git仓库下都有用。
在撤销修改一节中,我们知道,命令git reset HEAD file可以把暂存区的修改撤销掉(unstage),重新放回工作区。既然是一个unstage操作,就可以配置一个unstage别名:
$ git config --global alias.unstage 'reset HEAD'
当你敲入命令:
$ git unstage test.py
实际上Git执行的是:
$ git reset HEAD test.py
配置一个git last,让其显示最后一次提交信息:
$ git config --global alias.last 'log -1'
这样,用git last就能显示最近一次的提交:
$ git last
commit adca45d317e6d8a4b23f9811c3d7b7f0f180bfe2
Merge: bd6ae48 291bea8
Author: Michael Liao <askxuefeng@gmail.com>
Date: Thu Aug 22 22:49:22 2013 +0800
merge & fix hello.py
甚至还有人丧心病狂地把lg配置成了:
git config --global alias.lg "log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit"
来看看git lg的效果:
git-lg
image.png为什么不早点告诉我?别激动,咱不是为了多记几个英文单词嘛!
配置文件
配置Git的时候,加上--global是针对当前用户起作用的,如果不加,那只针对当前的仓库起作用。
配置文件放哪了?每个仓库的Git配置文件都放在.git/config文件中:
$ cat .git/config
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
ignorecase = true
precomposeunicode = true
[remote "origin"]
url = git@github.com:michaelliao/learngit.git
fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
remote = origin
merge = refs/heads/master
[alias]
last = log -1
别名就在[alias]后面,要删除别名,直接把对应的行删掉即可。
而当前用户的Git配置文件放在用户主目录下的一个隐藏文件.gitconfig中:
$ cat .gitconfig
[alias]
co = checkout
ci = commit
br = branch
st = status
[user]
name = Your Name
email = your@email.com
配置别名也可以直接修改这个文件,如果改错了,可以删掉文件重新通过命令配置。
坑
refusing to merge unrelated histories
解决方法
git pull --allow-unrelated-histories
通过廖雪峰老师的git教程,又重新学习了一遍