GIT 使用总结(一)

468 阅读9分钟

安装

   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",创建一个新仓库

   在Repository name填入仓库名,其他保持默认,点击"Create repository"

   提示可从这个仓库克隆出新仓库,也可以推把已有的仓库与之关联,并推送至此仓库

$ 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

   勾选Initialize this repository with a README,这样GitHub会自动为我们创建一个README.md文件。创建完毕后,可以看到README.md文件:

$ 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,就能确定当前分支,以及当前分支的提交点:

   每次提交,master分支都会向前移动一步,这样,随着你不断提交,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

   从现在开始,对工作区的修改和提交就是针对dev分支了,比如新提交一次后,dev指针往前移动一步,而master指针不变:

   把dev合并到master上,就是直接把master指向dev的当前提交,就完成了合并:

$ git checkout master
$ git merge dev
Updating d46f35e..b17d20e
Fast-forward
 readme.txt | 1 +
 1 file changed, 1 insertion(+)

   注意到上面的Fast-forward信息,Git告诉我们,这次合并是“快进模式”,也就是直接把master指向dev的当前提交,所以合并速度非常快。

   合并完分支后,至可以删除dev分支。删除dev分支就是把dev指针给删掉,删掉后,我们就剩下了一条master分支:

$ 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无法执行“快速合并”,只能手动合并

$ 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也可以看到分支的合并情况:

$ 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)

   禁用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.

相关文档参考