安装
git官网安装程序直接下载,完成后,菜单找到“Git->Git Bash",出现命令行窗口。
$ git config --global user.name "your name"
$ git config --global user.email "email@explame.com"
创建版本库
-
选择合适的地方,创建一个空目录
$ mkdir a //不包含中文
$ cd a
$ pwd //显示当前目录
-
git init 把这个目录变成Git客管路的仓库
$ git init
Initialized empty Git repository in /d/Users/EX-bianzhenzhen001/work/a/.git/
目录下多了一个.git 的目录,ls -ah可查看
-
文件添加到版本库
$ touch <file> // 新建文件,添加更改内容,cat <file1>[ <file2>],在屏幕显示文件1[和文件2]的内容
$ git status
$ git diff <file>
$ git add (<file>|-A|-u|.) | $ git checkout (-- <file>|.) //舍弃当前目录所有更改
$ git commit'message'
-
版本回退
$ git log //返回当前版本和之前所有提交记录
commit 1094adb7b9b3807259d8cb349e7df1d4d6477073 (HEAD -> master)
Author: your name <email@explame.com>
Date: Fri May 18 21:06:15 2018 +0800
append bbb
commit e475afc93c209a690c39c13a46716e8fa000c366
Author: your name <email@explame.com>
Date: Fri May 18 21:03:36 2018 +0800
add aaa
commit eaadf4e385e865d25c48e7ca9c8395c3f7dfaef0
Author: your name <email@explame.com>
Date: Fri May 18 20:59:18 2018 +0800
wrote a readme file
或者
$ git log --pretty=online // 查看缩略信息
$ git log --graph // 查看分支合并图
1094adb7b9b3807259d8cb349e7df1d4d6477073 (HEAD -> master) append bbb
e475afc93c209a690c39c13a46716e8fa000c366 add aaa
eaadf4e385e865d25c48e7ca9c8395c3f7dfaef0 wrote a readme file
回退
$ git reset --hard HEAD^ //上个版本
$ git reset --hard HEAD^^ //上上个版本
$ git reset --hard HEAD~100 //往上100个版本
$ git reset --hard HEAD 1094a //回退到某个版本
回退到上个版本(add aaa)后想恢复
//知道版本号
$ git reset --hard HEAD 1094a //回退到最开始的版本
//不知道版本号
$ git reflog //返回所以版本的操作日志
e475afc HEAD@{1}: reset: moving to HEAD^
1094adb (HEAD -> master) HEAD@{2}: commit: append bbb
e475afc HEAD@{3}: commit: add aaa
eaadf4e HEAD@{4}: commit (initial): wrote a readme file
-
工作区和暂缓区
-
撤销修改
$ cat readme.txt
//1.仅在工作区,未交到暂缓区
$ git checkout -- <file> //撤销工作区的修改,保持和本地仓库或者暂缓区一致
//2.仅在暂缓区,未提交仓库
$ git reset HEAD <file> //回到1,第二步按1场景操作
//3.仅在本地仓库,未提交远程
$ git reset HEAD^ //版本回退
-
删除文件
$ rm test.txt
$ git add/rm <file> // git rm test.txt
-
添加远程库
登录GitHub 右上角"Creat a new repo",创建一个新仓库
$ git remote add origin git@server-name:path/repo-name.git //先关联本地仓库,git@github.com:bian/test.git
$ git push -u origin master //-u,再关联本地分支
//相当于 $ git push origin master + $ git branch --set-upstream master origin/master
-
SSH警告
第一次使用clone或者push,会得到一个警告:
The authenticity of host 'github.com (xx.xx.xx.xx)' can't be established.
RSA key fingerprint is xx.xx.xx.xx.xx.
Are you sure you want to continue connecting (yes/no)?
这是因为Git使用SSH连接,而SSH连接在第一次验证GitHub服务器的Key时,需要你确认GitHub的Key的指纹信息是否真的来自GitHub的服务器,输入yes回车即可。
Git会输出一个警告,告诉你已经把GitHub的Key添加到本机的一个信任列表里了:
Warning: Permanently added 'github.com' (RSA) to the list of known hosts.
-
从远程库克隆
创建新仓库gitskills
$ git clone git@github.com:bian/gitskills //克隆至本地文件夹下,此时mater是自动关联的
$ cd gitskills
$ ls
README.md
GitHub给出的地址不止一个,还可以用https://github.com/michaelliao/gitskills.git这样的地址。实际上,Git支持多种协议,默认的git://使用ssh,但也可以使用https等其他协议,但通过ssh支持的原生git协议速度最快.
-
创建与合并分支
master分支是一条线,Git用master指向最新的提交,再用HEAD指向master,就能确定当前分支,以及当前分支的提交点:
创建新分支dev,Git新建了一个指针叫dev,指向master相同的提交,再把HEAD指向dev,表示切到dev上:
$ git checkout -b dev // git branch dev + git checkout dev
$ git branch // -r,查看远程分支;-a,查看所有的分支;无参数,本地分支;
//-v,查看各个分支最后一次提交对象信息
//-vv,查看对应关联远程分支
* dev
master
$ git checkout master
$ git merge dev
Updating d46f35e..b17d20e
Fast-forward
readme.txt | 1 +
1 file changed, 1 insertion(+)
注意到上面的Fast-forward信息,Git告诉我们,这次合并是“快进模式”,也就是直接把master指向dev的当前提交,所以合并速度非常快。
$ git branch -d dev //本次操作指令
$ git branch -D dev // -D ,在master分支merge之前强制删除
$ git branch -d -r dev // -r ,删除远程dev分支
-
解决冲突
新分支feature,改动test.txt文件,提交 切到master
$ git checkout master
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 1 commit.
(use "git push" to publish your local commits)
Git还会自动提示我们当前master分支比远程的master分支要超前1个提交。
改动test.txt文件,提交
现在,master分支和feature1分支各自都分别有新的提交,变成了这样:
$ git merge feature // --abort 退出merge,保留冲突
Auto-merging test.txt
CONFLICT (content): Merge conflict in test.txt
Automatic merge failed; fix conflicts and then commit the result.
git status也可以告诉我们冲突的文件:
$ git status
On branch master
Your branch is ahead of 'origin/master' by 2 commits.
(use "git push" to publish your local commits)
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: readme.txt
no changes added to commit (use "git add" and/or "git commit -a")
Git用<<<<<<<,=======,>>>>>>>标记出不同分支的内容,我们修改如下后保存:
<<<<<<< HEAD //当前分支名
XXXXX
=======
XXXXXXXXXXXX
>>>>>>> feature1 //合并分支名
现在,master分支和feature1分支变成了下图所示:
$ git log --graph --pretty=oneline --abbrev-commit
* cf810e4 (HEAD -> master) conflict fixed
|\
| * 14096d0 (feature) AND simple
* | 5dc6824 & simple
|/
* b17d20e branch test
* d46f35e (origin/master) remove test.txt
* b84166e add test.txt
* 519219b git tracks changes
* e43a48b understand how stage works
* 1094adb append GPL
* e475afc add distributed
* eaadf4e wrote a readme file
-
分支管理策略
通常,合并分支时,如果可能,Git会用Fastforward模式,但这种模式下,删除分支后,会丢掉分支信息。
如果要强制禁用Fastforward模式,Git就会在merge时生成一个新的commit,这样,从分支历史上就可以看出分支信息。
master分支merge dev:(fast forward)
$ git merge --no-ff -m"merge with np-ff" //因为本次合并要创建一个新的commit,所以加上-m参数
$ git log --graph --pretty=oneline --abbrev-commit
* e1e9c68 (HEAD -> master) merge with no-ff
|\
| * f52c633 (dev) add merge
|/
* cf810e4 conflict fixed
合并分支时,加上--no-ff参数就可以用普通模式合并,合并后的历史有分支,能看出来曾经做过合并,而fast forward合并就看不出来曾经做过合并。
-
bug分支
将工作区的内容不想add就先储存起来,等以后恢复现场后继续工作:
$ git stash
Saved working directory and index state WIP on dev: f52c633 add merge
修复master分支上的bug,从master临时拉取新的分支,最后合并到master
储存的内容恢复到工作区:
$ git stash list // stash内容
$ git stash apply //恢复后,stash内容并不删除,需要用git stash drop来删除
$ git stash apply stash@{0}
$ git stash drop
或者直接
$ git stash pop // 恢复的同时把stash内容也删了
On branch dev
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: hello.py
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: readme.txt
Dropped refs/stash@{0} (5d677e2ee266f39ea296182fb2354265b91b3b2a)
-
删除分支
已merge过的分支
$ git branch -d feature
未合并过的分支
$ git branch -d feature
error: The branch 'feature' is not fully merged.
If you are sure you want to delete it, run 'git branch -D feature'
销毁失败。Git友情提醒,feature-vulcan分支还没有被合并,如果删除,将丢失掉修改,如果要强行删除,需要使用大写的-D参数
$ git branch -D feature
-
多人协作
当你从远程仓库克隆时,实际上Git自动把本地的master分支和远程的master分支关联起来了,
默认情况下,只能看到本地的master分支
并且,远程仓库的默认名称是origin
$ git remote // 查看远程库信息
origin
$ git remote -v // 显示更详细的信息
origin git@github.com:michaelliao/learngit.git (fetch)
origin git@github.com:michaelliao/learngit.git (push)
上面显示了可以抓取和推送的origin的地址。如果没有推送权限,就看不到push的地址。
克隆远程库到本地,默认只有master,必须新建远程origin的dev到本地
克隆版本库的时候,所使用的远程主机自动被Git命名为origin。如果想用其他的主机名,需要用git clone命令的-o选项指定。
$ git clone -o jQuery https://github.com/jquery/jquery.git
$ git remote
jQuery
$ git remote show <主机名> // 可以查看该主机的详细信息
$ git remote add <主机名> <网址> // 命令用于添加远程主机
$ git remote rm <主机名> // 用于删除远程主机
$ git remote rename <原主机名> <新主机名> // 用于远程主机的改名
$ git checkout -b dev origin/dev
$ git add commit ... //一系列的操作提交
$ git pull origin dev
$ git push origin dev
关联本地和远程分支
$ git branch --set-upstream-to=origin/dev dev //直接 git pull/push
$ git pull
$ git push
-
rebase
每次pull后在push,分支变成这样:
$ git log --graph --pretty=oneline --abbrev-commit
* d1be385 (HEAD -> master, origin/master) init hello
* e5e69f1 Merge branch 'dev'
|\
| * 57c53ab (origin/dev, dev) fix env conflict
| |\
| | * 7a5e5dd add env
| * | 7bd91f1 add new env
| |/
* | 12a631b merged bug fix 101
|\ \
| * | 4c805e2 fix bug 101
|/ /
* | e1e9c68 merge with no-ff
|\ \
| |/
| * f52c633 add merge
|/
* cf810e4 conflict fixed
和远程同步后,本地更改再做两个提交:
$ git log --graph --pretty=oneline --abbrev-commit
* 582d922 (HEAD -> master) add author
* 8875536 add comment
* d1be385 (origin/master) init hello //远程同步
* e5e69f1 Merge branch 'dev'
|\
| * 57c53ab (origin/dev, dev) fix env conflict
| |\
| | * 7a5e5dd add env
| * | 7bd91f1 add new env
注意到Git用(HEAD -> master)和(origin/master)标识出当前分支的HEAD和远程origin的位置分别是582d922 add author和d1be385 init hello,本地分支比远程分支快两个提交
此时远程又提交:
$ git pull
$ git status
On branch master
Your branch is ahead of 'origin/master' by 3 commits.
(use "git push" to publish your local commits)
nothing to commit, working tree clean
加上刚才合并的提交,现在我们本地分支比远程分支超前3个提交。
$ git log --graph --pretty=oneline --abbrev-commit
* e0ea545 (HEAD -> master) Merge branch 'master' of github.com:michaelliao/learngit
|\
| * f005ed4 (origin/master) set exit=1
* | 582d922 add author
* | 8875536 add comment
|/
* d1be385 init hello
rebase操作可以把本地未push的分叉提交历史整理成直线:
$ git rebase // 会输出一大堆操作
$ git log --graph --pretty=oneline --abbrev-commit
* 7e61ed4 (HEAD -> master) add author
* 3611cfe add comment
* f005ed4 (origin/master) set exit=1
* d1be385 init hello
原本分叉的提交现在变成一条直线了!我们注意观察,发现Git把我们本地的提交“挪动”了位置,放到了f005ed4 (origin/master) set
exit=1之后,这样,整个提交历史就成了一条直线。rebase操作前后,最终的提交内容是一致的,但是,我们本地的commit修改内容已经变化了,它们的修改不再基于d1be385 init hello,而是基于f005ed4 (origin/master) set exit=1,但最后的提交7e61ed4内容是一致的。
推到远程:
$ git push
$ git log --graph --pretty=oneline --abbrev-commit
* 7e61ed4 (HEAD -> master, origin/master) add author
* 3611cfe add comment
* f005ed4 set exit=1
* d1be385 init hello
-
标签创建和操作
切到需要打标签的分支
$ git tag v1.0 // git tag <name>
$ git tag
v1.0
默认标签是打在最新提交的commit上
$ git log --pretty=oneline --abbrev-commit
12a631b (HEAD -> master, tag: v1.0, origin/master) merged bug fix 101
4c805e2 fix bug 101
e1e9c68 merge with no-ff
f52c633 add merge
cf810e4 conflict fixed
5dc6824 & simple
14096d0 AND simple
b17d20e branch test
d46f35e remove test.txt
b84166e add test.txt
519219b git tracks changes
e43a48b understand how stage works
1094adb append GPL
e475afc add distributed
eaadf4e wrote a readme file
要对add merge这次提交打标签,它对应的commit id是f52c633,敲入命令:
$ git tag v0.9 f52c633
$ git tag
v0.9
v1.0 // 标签不是按时间顺序列出,而是按字母排序的
$ git show v0.9 // 查看标签信息
commit f52c63349bc3c1593499807e5c8e972b82c8f286 (tag: v0.9)
Author: Michael Liao <askxuefeng@gmail.com>
Date: Fri May 18 21:56:54 2018 +0800
add merge
diff --git a/readme.txt b/readme.txt
还可以创建带有说明的标签,用-a指定标签名,-m指定说明文字:
$ git tag -a v0.1 -m"version 0.1 released" 1094adb
$ git show v0.1
tag v0.1
Tagger: Michael Liao <askxuefeng@gmail.com>
Date: Fri May 18 22:48:43 2018 +0800
version 0.1 released
commit 1094adb7b9b3807259d8cb349e7df1d4d6477073 (tag: v0.1)
Author: Michael Liao <askxuefeng@gmail.com>
Date: Fri May 18 21:06:15 2018 +0800
append GPL
diff --git a/readme.txt b/readme.txt
如果标签打错了,也可以删除:
$ git tag -d v0.1 // 因为创建的标签都只存储在本地,不会自动推送到远程。所以,打错的标签可以在本地安全删除。
推送某个标签到远程:
$ git push origin v1.0
Total 0 (delta 0), reused 0 (delta 0)
To github.com:bianzz/test.git
* [new tag] v1.0 -> v1.0
或者,一次性推送全部尚未推送到远程的本地标签:
$ git push origin --tags
Total 0 (delta 0), reused 0 (delta 0)
To github.com:bianzz/test.git
* [new tag] v0.9 -> v0.9
如果标签已经推送到远程,要删除远程标签就麻烦一点,先从本地删除:
然后,从远程删除。删除命令也是push,但是格式如下:
$ git tag -d v0.9
Deleted tag 'v0.9' (was f52c633)
$ git push origin :refs/tags/v0.9
To github.com:michaelliao/learngit.git
- [deleted] v0.9
-
忽略特殊文件
在Git工作区的根目录下创建一个特殊的.gitignore文件,然后把要忽略的文件名填进去,最后一步就是把.gitignore也提交到Git,就完成了
cat .gitignore
/major/node_modules/*
/node_modules/*
*.DS_Store
.buildpath
.project
.settings
.idea
*.swp
build
sftp-config.json
.tags
.tags_sorted_by_file
*.env
/nbproject/private/
/vendor/
*/npm-debug.log
有些时候,你想添加一个文件到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服务器
搭建Git服务器需要准备一台运行Linux的机器,强烈推荐用Ubuntu或Debian,这样,通过几条简单的apt命令就可以完成安装。
假设你已经有sudo权限的用户账号,下面,正式开始安装.
第一步,安装git:
$ sudo apt-get install git
第二步,创建一个git用户,用来运行git服务:
$ sudo adduser git
第三步,创建证书登录:
收集所有需要登录的用户的公钥,就是他们自己的id_rsa.pub文件,把所有公钥导入到/home/git/.ssh/authorized_keys文件里,一行一个。
第四步,初始化Git仓库:
先选定一个目录作为Git仓库,假定是/srv/sample.git,在/srv目录下输入命令:
$ sudo git init --bare sample.git
Git就会创建一个裸仓库,裸仓库没有工作区,因为服务器上的Git仓库纯粹是为了共享,所以不让用户直接登录到服务器上去改工作区,并且服务器上的Git仓库通常都以.git结尾。然后,把owner改为git:
$ sudo chown -R git:git sample.git
第五步,禁用shell登录:
出于安全考虑,第二步创建的git用户不允许登录shell,这可以通过编辑/etc/passwd文件完成。找到类似下面的一行:
git:x:1001:1001:,,,:/home/git:/bin/bash
改为:
git:x:1001:1001:,,,:/home/git:/usr/bin/git-shell
这样,git用户可以正常通过ssh使用git,但无法登录shell,因为我们为git用户指定的git-shell每次一登录就自动退出。
第六步,克隆远程仓库:
$ git clone git@server:/srv/sample.git
Cloning into 'sample'...
warning: You appear to have cloned an empty repository.
相关文档参考