git 学习使用总结

目录

  • git 学习使用总结

    • 一、通用的git命令集:
    • 二、常用命令
    • 三、Git Branch
      • 常用命令
      • Bug分支
      • 多人协作
    • 四、git diff
    • 五、在github中创建远程仓库,并略过初始化README
    • 六、git gc
      • 1、解压 .pack 文件
      • 2、清理垃圾对象
    • 七、Git Merge
      • 1、Git Merge - Fast Forward 快速合并
      • 2、Git Merge - 3 way merge 三分合并
      • 3、rebase
    • 八、Tags Operation: 标签操作
    • 九、Git fetch & git pull
    • 十、push使用详解
    • hooks
    • 十一、不常用的命令
    • 十二、 创建ssh key

git 学习使用总结

基本命令格式:

usage: git  []

一、通用的git命令集:

一、开始一个工作区(参见:git help tutorial)

clone			    克隆一个仓库到一个新的目录中。
init				创建一个空的Git仓库,或者重新初始化一个存在的仓库。

二、对当前的变化进行处理(参见:git help everyday)

add					把文件的内容加入到索引区(index)
mv					移动或重命名一个文件、目录或链接
restore				恢复工作的文件树
rm					从工作目录树和索引区中删除文件
sparse-checkout		初始化和修改稀疏checkout

三、检查历史和状态(参见:git help revisions)

bisect				使用二进制搜索查找引入错误的提交
diff				显示commits之间,或commit与工作目录区等的改变内容
grep				打印匹配模式的行
log					显示提交(commit)的日志
show				显示对象的不同类型
status				显示工作目录区的状态

四、增长、标记和微调通用的历史

branch				列出、创建或删除分支
commit				把变化的内容记录到仓库中
merge				把两个或者更多的开发历史加在一起
rebase				在另一个基层的顶部重新应用提交
reset				重置当前的HEAD到指定的状态
tag					创建、列出、删除或验证带GPG标记的tag对象

五、协作(参见: git help workflows)

fetch				从另一个仓库中下载对象和引用
pull				从另一个仓库中或者当地分支fetch和整合
push				跟相关的对象一起更新远程的引用

git help -a 和 git help -g 列出可用的子命令和一些概念指导。
参见 git help  或 git help  阅读指定的子命令或概念。
git help git 可以查看该系统的概况。

二、常用命令

初始化一个仓库

git init                   初始化
git status                 查看状态
git add [filename|.]   	   把工作区的文件或所有文件添加到索引区。	                              
git commit -m 'some msg'   提交到本地仓库中。把索引区的内容提交到本地仓库,如果两个文件的内容完全相同,
								在索引区只会创建一个blob对象。
git gc                     把blob对象进行压缩和垃圾对象进行清理
git branch xxx             创建分支
git checkout xxx           切换到xxx分支
git checkout b xxx         创建并切换到xxx分支
git checkout -- file       可以丢弃工作区的修改。git checkout -- file命令中的 -- 很重要,
												没有 --,就变成了“切换到另一个分支”的命令
git reset HEAD       可以把暂存区的修改撤销掉(unstage),重新放回工作区;
					       git reset命令既可以回退版本,也可以把暂存区的修改回退到工作区。
						   当我们用HEAD时,表示最新的版本。
$ git rm         从版本库中删除该文件.小提示:先手动删除文件,然后使用git rm 
								和git add效果是一样的。
另一种情况是删错了,因为版本库里还有呢,所以可以很轻松地把误删的文件恢复到最新版本:
$ git checkout -- test.txt
git checkout其实是用版本库里的版本替换工作区的版本,无论工作区是修改还是删除,都可以“一键还原”。
注意:从来没有被添加到版本库就被删除的文件,是无法恢复的!

$ cat .git/HEAD
ref: refs/heads/master     HEAD目前指向的是master分支。  

$ cat .git/refs/heads/master   d0bde7.....  说明master指向的是一个commit对象。
                                   而commit指向的是一个tree,tree指向的是文件对象。

如果在工作区中创建文件夹,并使用以下命令:
git add folder1/
	只会创建其中的文件,文件夹的信息没有包含其中,但会出现在objects/文件中,例如
folder1/file3.txt
	可以使用 git ls-files -s 结果中查询的
	但是在执行 git commit -m 'xxx' 后,会创建一个tree对象,对应于这个文件夹。

Git中的文件状态,一共有四种:

Untracked 
Modified 
Staged 
Unmodified 'both modified'

查看提交log

git log
	可以查看commit提交的历史,以及可以看到HEAD所指向的commit
	可以加选项: --pretty=oneline   ,这样输入比较简洁。

Branches
	分支就是指向commit的命令指针。
	
Master branch and HEAD
	Master是一个分支,默认是规范的主线分支。
	HEAD是一个特殊的指针,与当前活动的分支相关联,永远指向最新的commit。

三、Git Branch

常用命令

$ git branch:                   列出已经有的分支
$ git branch :    创建一个名为branch_name的分支,如果已经存在,会出现一个错误。
$ git branch -d    可以删除已经merge后的分支
$ git branch -D    可以删除还没有merge后的分支
$ git checkout dev:             切换到dev分支
$ git checkout -b dev:          创建并切换到新的div分支
$ git switch -c dev             创建并切换到新的dev分支
$ git switch master             直接切换到已有的master分支

使用新的git switch命令,比git checkout要更容易理解。

$ cat .git/HEAD
      ref: refs/heads/dev

小结:

Git鼓励大量使用分支:	
查看分支:             git branch	
创建分支:             git branch 	
切换分支:             git checkout 或者git switch 	
创建+切换分支:        git checkout -b 或者git switch -c 	
合并某分支到当前分支:  git merge 	
删除分支:             git branch -d 

Bug分支

当你接到一个修复一个代号101的bug的任务时,很自然地,你想创建一个分支issue-101来修复它,但是,等等,当前正在dev上进行的工作还没有提交:

$ git status   有些内容还没有提交,也不想提交。

Git还提供了一个stash功能,可以把当前工作现场“储藏”起来,等以后恢复现场后继续工作:

$ git stash
Saved working directory and index state WIP on dev: f52c633 add merge

现在,用git status查看工作区,就是干净的(除非有没有被Git管理的文件),因此可以放心地创建分支来修复bug。
首先确定要在哪个分支上修复bug,假定需要在master分支上修复,就从master创建临时分支:

$ git checkout master
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 6 commits.
  (use "git push" to publish your local commits)

$ git checkout -b issue-101
Switched to a new branch 'issue-101'

现在修复bug,然后提交:

$ git add readme.txt 
$ git commit -m "fix bug 101"

修复完成后,切换到master分支,并完成合并,最后删除issue-101分支:

$ git switch master
...

$ git merge --no-ff -m "merged bug fix 101" issue-101
...

现在,是时候接着回到dev分支干活了!

$ git switch dev
Switched to branch 'dev'

$ git status
On branch dev
nothing to commit, working tree clean

工作区是干净的,刚才的工作现场存到哪去了?用git stash list命令看看:

$ git stash list
stash@{0}: WIP on dev: f52c633 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 list查看,然后恢复指定的stash,用命令:

$ git stash apply stash@{0}

在master分支上修复了bug后,我们要想一想,dev分支是早期从master分支分出来的,所以,这个bug其实在当前dev分支上也存在。

同样的bug,要在dev上修复,我们只需要把4c805e2 fix bug 101这个提交所做的修改“复制”到dev分支。注意:我们只想复制4c805e2 fix bug 101这个提交所做的修改,并不是把整个master分支merge过来。

为了方便操作,Git专门提供了一个cherry-pick命令,让我们能复制一个特定的提交到当前分支:

$ git branch
* dev
  master
  
$ git cherry-pick 4c805e2
[master 1d4b803] fix bug 101
 1 file changed, 1 insertion(+), 1 deletion(-)

Git自动给dev分支做了一次提交,注意这次提交的commit是1d4b803,它并不同于master的4c805e2,因为这两个commit只是改动相同,但确实是两个不同的commit。用git cherry-pick,我们就不需要在dev分支上手动再把修bug的过程重复一遍。

有些聪明的童鞋会想了,既然可以在master分支上修复bug后,在dev分支上可以“重放”这个修复过程,那么直接在dev分支上修复bug,然后在master分支上“重放”行不行?当然可以,不过你仍然需要git stash命令保存现场,才能从dev分支切换到master分支。

小结

修复bug时,我们会通过创建新的bug分支进行修复,然后合并,最后删除;

当手头工作没有完成时,先把工作现场git stash一下,然后去修复bug,修复后,再git stash pop,回到工作现场;

在master分支上修复的bug,想要合并到当前dev分支,可以用git cherry-pick 命令,把bug提交的修改“复制”到当前分支,避免重复劳动。

多人协作

当你从远程仓库克隆时,实际上Git自动把本地的master分支和远程的master分支对应起来了,并且,远程仓库的默认名称是origin。

要查看远程库的信息,用git remote:

$ git remote
origin

或者,用git remote -v显示更详细的信息:

$ git remote -v
...
...

上面显示了可以抓取和推送的origin的地址。如果没有推送权限,就看不到push的地址。

推送分支

推送分支,就是把该分支上的所有本地提交推送到远程库。推送时,要指定本地分支,这样,Git就会把该分支推送到远程库对应的远程分支上:

$ git push origin master

如果要推送其他分支,比如dev,就改成:

$ git push origin dev

但是,并不是一定要把本地分支往远程推送,那么,哪些分支需要推送,哪些不需要呢?

master分支是主分支,因此要时刻与远程同步;

dev分支是开发分支,团队所有成员都需要在上面工作,所以也需要与远程同步;

bug分支只用于在本地修复bug,就没必要推到远程了,除非老板要看看你每周到底修复了几个bug;

feature分支是否推到远程,取决于你是否和你的小伙伴合作在上面开发。

总之,就是在Git中,分支完全可以在本地自己藏着玩,是否推送,视你的心情而定!

抓取分支

多人协作时,大家都会往master和dev分支上推送各自的修改。

现在,模拟一个你的小伙伴,可以在另一台电脑(注意要把SSH Key添加到GitHub)或者同一台电脑的另一个目录下克隆:

$ git clone git@github.com:michaelliao/learngit.git
......

当你的小伙伴从远程库clone时,默认情况下,你的小伙伴只能看到本地的master分支。不信可以用git branch命令看看:

$ git branch
* master

现在,你的小伙伴要在dev分支上开发,就必须创建远程origin的dev分支到本地,于是他用这个命令创建本地dev分支:

$ git checkout -b dev origin/dev

现在,他就可以在dev上继续修改,然后,时不时地把dev分支push到远程:

$ git add env.txt

$ git commit -m "add env"
...
$ git push origin dev
...

你的小伙伴已经向origin/dev分支推送了他的提交,而碰巧你也对同样的文件作了修改,并试图推送:

$ cat env.txt
env

$ git add env.txt

$ git commit -m "add new env"
...

$ git push origin dev
To github.com:michaelliao/learngit.git
 ! [rejected]        dev -> dev (non-fast-forward)
error: failed to push some refs to 'git@github.com:michaelliao/learngit.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

推送失败,因为你的小伙伴的最新提交和你试图推送的提交有冲突,解决办法也很简单,Git已经提示我们,先用git pull把最新的提交从origin/dev抓下来,然后,在本地合并,解决冲突,再推送:

$ git pull
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  

If you wish to set tracking information for this branch you can do so with:

git branch --set-upstream-to=origin/ dev

git pull也失败了,原因是没有指定本地dev分支与远程origin/dev分支的链接,根据提示,设置dev和origin/dev的链接:

$ git branch --set-upstream-to=origin/dev dev
Branch 'dev' set up to track remote branch 'dev' from 'origin'.

再pull:

$ git pull
...

这回git pull成功,但是合并有冲突,需要手动解决,解决的方法和分支管理中的解决冲突完全一样。解决后,提交,再push:

$ git commit -m "fix env conflict"
[dev 57c53ab] fix env conflict

$ git push origin dev

因此,多人协作的工作模式通常是这样:

  1. 首先,可以试图用git push origin 推送自己的修改;

  2. 如果推送失败,则因为远程分支比你的本地更新,需要先用git pull试图合并;

  3. 如果合并有冲突,则解决冲突,并在本地提交;

  4. 没有冲突或者解决掉冲突后,再用git push origin 推送就能成功!

如果git pull提示no tracking information,则说明本地分支和远程分支的链接关系没有创建,用命令git branch --set-upstream-to origin/。

这就是多人协作的工作模式,一旦熟悉了,就非常简单。

小结

  • 查看远程库信息,使用git remote -v;

  • 本地新建的分支如果不推送到远程,对其他人就是不可见的;

  • 从本地推送分支,使用git push origin branch-name,如果推送失败,先用git pull抓取远程的新提交;

  • 在本地创建和远程分支对应的分支,使用git checkout -b branch-name origin/branch-name,
    本地和远程分支的名称最好一致;

  • 建立本地分支和远程分支的关联,使用git branch --set-upstream branch-name origin/branch-name;

  • 从远程抓取分支,使用git pull,如果有冲突,要先处理冲突。

不能删除当前分支,必须切换到其他分支才行

$ git checkout master
$ git branch -D dev:    这个命令不会提示是否已经merge,不建议使用,使用-d较好。
               但是如果使用-D dev后,分支是被删除了,其他的commit、tree对象不会被同时删除。

我们也可以切换到某个commit上,这样HEAD就直接指向了这个commit,而不是有名称的分支

$ git checkout 2e4624
	这时打印出来的内容很多,提示在'detached HEAD'状态,可以使用
git switch -c 
	或者撤销这个操作
git switch -

$ git log
	此时可以看到的commit日志就会少许多

	此时由于没有分支名称,我们也可以使用较新的git switch,也可以使用
$ git checkout -b 
	来创建一个分支,并切换到该分支上。

	如果我们要找到比当前master分支前面的无分支名的commit,可以使用:
$ git reflog
	就会打印出所有HEAD的切换的日志,就可以找到无名的commit,然后
$ git checkout 
$ git branch -b dev

四、git diff

可以对比工作区和索引区中文件不同之处,
$ git diff --cached
	可以对比索引区和本地仓库之间的文件不同。
	当然如果使用vscode显示更为明显。
	```		
$ git diff HEAD -- 
	查看工作区和版本库里面最新版本的区别
	```

五、在github中创建远程仓库,并略过初始化README

配置git全局变量:

$ git config --global user.name "Your Name"
$ git config --global user.email "email@example.com"

先在github上创建一个新的仓库,会提示三种不同的后续操作,分别为:创建新的仓库,从现存的仓库推送,从其他仓库中导入。

SSH:
git remote add origin git@github.com:xxx/yyy.git
HTTPS:
git remote add origin https://github.com/xxx/yyy.git
	远程库的名字就是origin,这是Git默认的叫法,也可以改成别的,
					但是origin这个名字一看就知道是远程库。

执行该命令后,会在.git/config文件中增加:
[remote "origin"]
		url = https:/github.com/xxx/yyy.git
		fetch = +refs/heads/*:refs/remotes/origin/*

git push -u origin master
	就会把本地仓库代码的送到远程origin master分支的仓库中。第一次推送master分支时,加上了-u参数,
		Git不但会把本地的master分支内容推送的远程新的master分支,还会把本地的master分支和
		远程的master分支关联起来,在以后的推送或者拉取时就可以简化命令。

以后使用这个命令可以推送master到远程库中:
$ git push origin master

删除远程库
如果添加的时候地址写错了,或者就是想删除远程库,可以用git remote rm 命令。
		使用前,建议先用git remote -v查看远程库信息:
$ git remote -v
origin  git@github.com:/.git (fetch)
origin  git@github.com:/.git (push)

然后,根据名字删除,比如删除origin:	
$ git remote rm origin

此处的“删除”其实是解除了本地和远程的绑定关系,并不是物理上删除了远程库。远程库本身并没有任何改动。
		要真正删除远程库,需要登录到GitHub,在后台页面找到删除按钮再删除。

六、git gc

用于压缩 .git/objects/ 的对象

所有的对象都会把压缩到 .git/objects/pack 中
git help gc
git gc:		清除不需要的文件和优化本地仓库
ls -lh .git/objects/pack
显示两个文件:
.idx 和 .pack 文件
		所有的objects对象都被压缩到.pack文件中, .idx文件中记录相关信息,才能在
		一个.pack中查询到所有被压缩的文件信息。

可以使用

git verify-pack -v .git/objects/pack/pack-2e....3.idx
		会显示出来所有的commit、tree和blob对象的信息
git-verify-pack: 验证压缩的Git归档文件。

1、解压 .pack 文件

可以使用

git unpack-objects < path to .pack
		注意,manual中可以看出,如果压缩文件在原来的目录中,使用该命令不会解压。

2、清理垃圾对象

1、产生垃圾对象的原因之一是,在commit之前对同一个文件进行多次修改,并且都添加到索引区,
那么,前面几次的对象没有被引用,就是垃圾对象。

此时可以使用:
git gc
			有用的对象都会被压缩到 .git/objects/pack/ 中
			没有被引用的对象任然会留在 .git/objects/ 目录中。
可以使用:
git prune:  从对象数据库中修剪掉所有不能达到的对象。注意,大多情况下,用户应该运行git gc,
				会调用git prune命令。
git fsck: 打印出dangling blob对象(就是没有引用的垃圾对象)
git prune -n: 其作用于git fsck相似。

2、删除分支而产生的垃圾对象

git checkout -b tmp
		创建并切换到tmp分支,然后新建文件,git add . , git commit -m '1st commit from tmp'
git checkout master
		此时会产生三个对象,分别是:blob, tree, commit 对象。
git branch -D tmp: 强行删除tmp分支
		这时,该三个对象不会受到影响,就是使用 git prune命令也不能删除它们。
		
如果运行 git gc 也会把这三个对象压缩到 .pack 文件中,说明不认为是垃圾对象

如果我们一定要删除这三个对象:参考该网页:	
https://stackoverflow.com/questions/3797907/how-to-remove-unused-objects-from-a-git-repository
git -c gc.reflogExpire=0 -c gc.reflogExpireUnreachable=0 \
	-c gc.rerereresolved=0 -c gc.rerereunresolved=0 \
	-c gc.pruneExpire=now gc "$@"
	
这样就可以删除这些对象!

七、Git Merge

1、Git Merge - Fast Forward 快速合并

场景:

在当前master上产生一个分支,例如
git checkout -b bugfix
然后新建文件,git add , git commit 后,进行合并操作,前提是要切换到master分支
git checkout master
git merge bugfix
这样,就是把master指针从原来的位置指向bugfix指向的commit。
此时,.git/HEAD指向master分支,.git/ORIG_HEAD指向前一次master分支。有利于回滚操作。
git reset ORIG_HEAD
此时会把最新的commit对象予以删除,新建的文件也处于 Untracked 状态。

合并分支时,加上--no-ff(Fast forward)参数就可以用普通模式合并,合并后的历史有分支,能看出来曾经做过合并,而fast forward合并就看不出来曾经做过合并。

2、Git Merge - 3 way merge 三分合并

场景:

两个分支并行进行前进,都有了commit后进行merge操作。
git merge bugfix

会出现一个界面需要进行处理,即git需要生成一个commit,这个界面就是要输入一个msg。
git log

可以看到master已经指向最新的commit
git cat-file -p 32299

可以看到有两个parent,及两个父commit。即一个是.git/refs/heads/bugfix, 另一个是
.git/refs/heads/ORIG_HEAD

这种关系从
git log
上看不出来,需要其他工具进行。例如:sourcetree

如果出现同时修改同一个文件test.txt时,需要手工解决冲突

git merge bugfix
会提示合并失败,需要手动解决冲突,然后提交。
git status
会提示一些信息,并有一行提示
both Modified: test.txt
说明两个分支都修改了这个文件。
nvim test.txt
可以看到有
\<<<<<<< HEAD
\...
\=======
...
\>>>>>>> bugfix

git ls-files -s
100644 9dae...   1 test.txt
100644 2534...   2 test.txt
100644 4c53...   3 test.txt
其中文件名前面的数字是版本号。
如果把冲突解决好后(可以使用vscode较方便),然后git add test.txt
git ls-files -s
100644 b176...   0 test.tx如果把冲突解决好后(可以使用vscode较方便),然后git add test.txt
git ls-files -s
100644 b176...   0 test.txtt
git commit
打开编辑器界面,修改commit的msg信息,保存后就会提交

用带参数的git log也可以看到分支的合并图:
$ git log --graph --pretty=oneline --abbrev-commit

git rebase:如果其他分支有master最新的数据,就可以实现fast farword

而git rebase master 就是实现让其他要进行合并的分支有最新的数据。
当然在这个操作时,也会有可能出现冲突的,也需要进行手动解决。
但是如果该分支其他人也在使用,不推荐使用。

3、rebase

在上一节我们看到了,多人在同一个分支上协作时,很容易出现冲突。即使没有冲突,后push的童鞋不得不先pull,在本地合并,然后才能push成功。

每次合并再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的提交历史不能是一条干净的直线?其实是可以做到的!

Git有一种称为rebase的操作,有人把它翻译成“变基”。

先不要随意展开想象。我们还是从实际问题出发,看看怎么把分叉的提交变成直线。

在和远程分支同步后,我们对hello.py这个文件做了两次提交。用git log命令看看:

$ 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 push origin master
To github.com:michaelliao/learngit.git
 ! [rejected]        master -> master (fetch first)
error: failed to push some refs to 'git@github.com:michaelliao/learngit.git'
hint: Updates were rejected because the remote contains work that you do
hint: not have locally. This is usually caused by another repository pushing
hint: to the same ref. You may want to first integrate the remote changes
hint: (e.g., 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

很不幸,失败了,这说明有人先于我们推送了远程分支。按照经验,先pull一下:

$ git pull
...

再用git status看看状态:

$ 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看看:

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

对强迫症童鞋来说,现在事情有点不对头,提交历史分叉了。如果现在把本地分支push到远程,有没有问题?

有!

这个时候,rebase就派上了用场。我们输入命令git rebase试试:

$ git rebase
First, rewinding head to replay your work on top of it...
Applying: add comment
Using index info to reconstruct a base tree...
M	hello.py
Falling back to patching base and 3-way merge...
Auto-merging hello.py
Applying: add author
Using index info to reconstruct a base tree...
M	hello.py
Falling back to patching base and 3-way merge...
Auto-merging hello.py

输出了一大堆操作,到底是啥效果?再用git log看看:

$ 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内容是一致的。

这就是rebase操作的特点:把分叉的提交历史“整理”成一条直线,看上去更直观。缺点是本地的分叉提交已经被修改过了。

最后,通过push操作把本地分支推送到远程:

Mac:~/learngit michael$ git push origin master
Counting objects: 6, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (5/5), done.
Writing objects: 100% (6/6), 576 bytes | 576.00 KiB/s, done.
Total 6 (delta 2), reused 0 (delta 0)
remote: Resolving deltas: 100% (2/2), completed with 1 local object.
To github.com:michaelliao/learngit.git
   f005ed4..7e61ed4  master -> master

再用git log看看效果:

$ git log --graph --pretty=oneline --abbrev-commit
* 7e61ed4 (HEAD -> master, origin/master) add author
* 3611cfe add comment
* f005ed4 set exit=1
* d1be385 init hello

远程分支的提交历史也是一条直线。

小结

rebase操作可以把本地未push的分叉提交历史整理成直线;

rebase的目的是使得我们在查看历史提交的变化时更容易,因为分叉的提交需要三方对比。

八、Tags Operation: 标签操作

-a, --annotate
	生成一个无符号的带注释的标签对象
-s, --sign
	使用默认电子邮件地址的密钥创建一个GPG签名的标记。
-d, --delete 
	删除标签

git tag  生成轻量化的tag: 只会在/.git/refs/tags/下生成一个文件。
	git tag v1.0
git tag -a  -m :这种tag会包含meta-data数据(email 作者 时间 时区etc.)
	还会生成一个tag对象。
	git tag -a v1.0 -m "version 1.0"
git tag -a  : 为旧的commit创建带标签的tag
	git tag -a v1.0 236e1e......
	git cat-files -p : 查询到具体的信息,与commit类似。
此时,运行
git tag -d v1.0
只会删除tags/下的文件,不会删除那个tag对象。

九、Git fetch & git pull

如果远程仓库进行了一些commit,而本地仓库没有变化,这是远程的master超前本地一次提交
git fetch:就会把远程的最新提交同步到本地,而此时本地的master没有变化
git merge origin/master:合并同步过来的commit
git pull: 相当于以上两步

如果出现"3 way merge"就与前面所得一样,可能有冲突,也可能没有,处理后,进行一个新的commit,会比远程仓库的commit提前了2步。

git branch -vv
可以显示超前于origin/master: ahead 2等信息。
git push origin master: 就可以同步远程的仓库。

git remote show origin
显示远程的信息、分支等和一些提示
git fetch: 会在本地多出了文件 .git/FETCH_HEAD
cat .git/FETCH_HEAD
显示远程仓库最新的分支信息

如果远程仓库有几个分支,在git fetch时,本地的.git/FETCH_HEAD内的显示顺序与
在哪个本地分支进行git fetch有关。如果直接运行 git pull就会只merge该分支,其他分支不会merge。

git reset --hard ORIG_HEAD
可以回滚到前面的版本

上一个版本就是HEAD^,上上一个版本就是HEAD^^,当然往上100个版本写100个^比较容易数不过来,所以写成HEAD~100

$ git reset --hard HEAD^

如果想要回到未来的版本,可以使用get reflog(记录你的每一次命令)找到这个commit id,然后再 git reset --hard commit_id

git branch -vv: 可以查看本地与远程的分支是否同步。

如果远程仓库中删除了分支dev,使用:

$ git fetch
不能删除本地已经存在的dev,
$ git fetch --prune
$ git remote prune
以上两个命令都可以实现删除本次的dev

十、push使用详解

如果本地有新的分支,而远程没有直接使用
git push 不能成功,提示使用 git push --set-upstream origin feature-1
这是因为本地分支与远程分支没有关联。也可以使用
git push origin feature-1
也行。其中--set-upstream(-u)用于本地与远程进行关联。此时
git branch -vv
可以看到 feature-1与远程仓库的feaure-1没有关联。
git push origin -d feature-1
可以通过命令行删除远程仓库中的feature-1分支。
git push -u origin feature-1
此时使用
git branch -vv
可以看到feature-1与远程origin的feature-1相关联,关联之后,
		以后再次push时,就可以简单的使用
git push
就可以。

hooks

hook中文意思是钩子,我的理解是关卡,就是在操作过程中,在前面或后面进行一些检查。
例如,使用python编程,没有符合相关的编程格式,如果使用了pre-commit钩子,提交不能成功。
	当然,需要按照不同语言进行设置和配置一些文件。

十一、不常用的命令

对仓库中的对象提供内容或类型和大小的信息
git cat-file [options] 
	-t 显示由标识的对象的类型,而不是内容。
	-s 显示由标识的对象的大小,而不是内容。
	-p 根据其类型美化打印其的内容
其中的可以由对象的sha的前面6个值就可以标识。
其产生的sha值由以下内容生成:
"blob len\0content"
其blob是对象的类型,len是文件的大小,content是文件的内容。
object的类型有:blob, commit, tree

$ echo -e "blob 10\0hello git" | shasum
$ echo "blob 10\0hello git" | shasum
这个方法可以计算出来sha1的值。8d0e41234f24b6da002d962a26c2495ea16a425f
所以,可以使用以下方法查到结果:
$ git cat-file -t 8d0e
blob
$ git cat-file -p 8d0e
hello git
$ git cat-file -s 8d0e
10
该文件是被压缩后的二进制文件,可以采用这个方法:
$ python3
>>> import zlib
>>> compressed_contents = open('0e41234f24b6da002d962a26c2495ea16a425f', 'rb').read()
>>> zlib.decompress(compressed_contents)
b'blob 10\x00hello git\n'

通过git add filename 会生成blob对象,但是其中没有记录其文件名,
文件名信息是保存在 .git/index中,这是个二进制文件,可以使用以下命令查看:
$ git ls-files
hello.txt
$ git ls-files -s
100644 8d0e41234f24b6da002d962a26c2495ea16a425f 0   hello.txt

git-ls-files 显示索引区和工作区中的有关文件的信息
其中参数
-s, --stage
	显示索引区内容的模式位,对象名称和索引sha值。
 
  

十二、 创建ssh key

$ ssh-keygen -t rsa -C "youremail@example.com"

使用ssh-kengen会在~/.ssh/目录下生成两个文件,不指定文件名和密钥类型的时候,默认生成的两个文件是:

id_rsa 第一个是私钥文件

id_rsa.pub 第二个是公钥文件

有许多内容参考廖雪峰的官方网站,也参考了卖逗搞IT。

你可能感兴趣的:(git,git,github)