Loading

git.md

Git 使用

诞生

  1. BitMover公司收回Linux社区对BitKeeper的免费使用权。

  2. Linus花了两周时间自己用C写了一个分布式版本控制系统,这就是Git!一个月之内,Linux系统的源码已经由Git管理了

分布式

  • 集中式版本控制系统:版本库是集中存放在中央服务器的,而干活的时候,用的都是自己的电脑,所以要先从中央服务器取得最新的版本,然后开始干活,干完活了,再把自己的活推送给中央服务器。中央服务器就好比是一个图书馆,你要改一本书,必须先从图书馆借出来,然后回到家自己改,改完了,再放回图书馆。
  • 分布式版本控制系统: 没有“中央服务器”,每个人的电脑上都是一个完整的版本库。分布式版本控制系统通常也有一台充当“中央服务器”的电脑,但这个服务器的作用仅仅是用来方便“交换”大家的修改,没有它大家也一样干活,只是交换修改不方便而已。

创建版本库(respository)

mkdir learngit
cd learngit
git init      

之后,会多出.git目录

git add .  # 将 . 文件夹添加到仓库
git commit # 提交至仓库

时光机穿梭

  • 当前状态:git status

  • 查看更改具体内容: git diff

版本回退

commit之后,相当于一个快照

  • 查看commit历史: git log

    查看简略日志: git log --pretty=oneline

    当前版本用HEAD表示

  • 版本回退: git reset --hard HEAD^ # HEAD^表示当前版本的前一个版本

    再次返回当前版本,找到SHA1值

    git reset --hard 70131

    • 查找SHA1值:

      git reflog # reference logs

Git的版本回退速度非常快,因为Git在内部有个指向当前版本的HEAD指针,当你回退版本的时候,Git仅仅是把HEAD从指向改变,再把工作区的文件更新

工作区(Working Directory)

就是你在电脑里能看到的目录,比如我的learngit文件夹就是一个工作区

版本库(Repository)

Git的版本库里存了很多东西,其中最重要的就是称为stage(或者叫index)的暂存区,还有Git为我们自动创建的第一个分支master,以及指向master的一个指针叫HEAD

git add实际就是把文件修改添加到暂存区stage

git commit实际是将stage提交到当前分支master

管理修改

Git跟踪并管理的是修改,而非文件。

git diff HEAD 命令可以查看工作区和版本库里面最新版本的区别

撤销修改

  1. 还未提交到暂存区

    最新的git合并了checkout和reset

    原来的git checkout 可以使用 git restore 代替

    原来的git reset HEAD 可以使用 git restore --staged 代替

git checkout -- readme.txt

命令git checkout -- readme.txt意思就是,把readme.txt文件在工作区的修改全部撤销,这里有两种情况:

一种是readme.txt自修改后还没有被放到暂存区,现在,撤销修改就回到和版本库一模一样的状态;

一种是readme.txt已经添加到暂存区后,又作了修改,现在,撤销修改就回到添加到暂存区后的状态。

总之,就是让这个文件回到最近一次git commitgit add时的状态。

git checkout -- file命令中的--很重要,没有--,就变成了“切换到另一个分支”的命令,我们在后面的分支管理中会再次遇到git checkout命令。

  1. 已经提交到暂存区

    git restore --staged index.htmlgit reset HEAD index.html

  2. 已经提交到版本库

    进行版本回退

删除文件

  1. 添加新文件

    touch 1.txt && git add 1.txt

  2. 从版本库中删除该文件: git rm test.txt

git rm file并且git commit并不是删除了版本库里的某个版本号,而是对工作目录下的删除操作进行了一个记录,会在仓库里生成一个新的版本号,在该版本下没有该文件。但是可以用git reset --hard commit_id进行版本回退,回退到有这个文件的版本号。而git checkout commit_id file命令是从含有该文件的旧版本号里把该文件拿出来,放到现版本里,版本号不改变。

远程管理

  1. 在github上新建仓库

  2. git remote add origin git@github.com:michaelliao/learngit.git

  3. git push -u origin master,将本地的master分支推送到远程origin

    由于远程库是空的,我们第一次推送master分支时,加上了-u参数,Git不但会把本地的master分支内容推送的远程新的master分支,还会把本地的master分支和远程的master分支关联起来,在以后的推送或者拉取时就可以简化命令。

  4. 之后提交:git push origin master

  5. 下载:git clone git://XXXXXXXXXX

分支管理

创建与合并分支

HEAD严格来说不是指向提交,而是指向mastermaster才是指向提交的,所以,HEAD指向的就是当前分支。

git-br-initial

当我们创建新的分支,例如dev时,Git新建了一个指针叫dev,指向master相同的提交,再把HEAD指向dev,就表示当前分支在dev上:git-br-create

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

git-br-dev-fd

假如我们在dev上的工作完成了,就可以把dev合并到master上。Git怎么合并呢?最简单的方法,就是直接把master指向dev的当前提交,就完成了合并:

git-br-ff-merge

所以Git合并分支也很快!就改改指针,工作区内容也不变!

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

git-br-rm

# 创建 dev 分支同时切换至dev, -b == branch + checkout
git checkout -b dev 
# 或者
git switch -c dev
# 查看当前分支
git branch
# 切换分支
git checkout dev 
# 或者
git switch dev
# 合并分支
git merge dev
# 删除分支
git branch -d dev

解决冲突

  1. 创建新分支feature

    git switch -c feature1

  2. 在新分支上修改:

    git add . && git commit -m 'modified something'

  3. 切换master分支

    git switch master

  4. 修改文件:

    git add . && git commit -m 'modified other somthing'

现在,master分支和feature1分支各自都分别有新的提交,变成了这样:

git-br-feature1

这种情况下,Git无法执行“快速合并”,只能试图把各自的修改合并起来,但这种合并就可能会有冲突,我们试试看:

$ git merge feature1
Auto-merging readme.txt
CONFLICT (content): Merge conflict in readme.txt
Automatic merge failed; fix conflicts and then commit the result.

现在,master分支和feature1分支变成了下图所示:

git-br-conflict-merged

删除feature1分支:$ git branch -d feature1

git log --graph命令可以看到分支合并图。

分支策略

在实际开发中,我们应该按照几个基本原则进行分支管理:

首先,master分支应该是非常稳定的,也就是仅用来发布新版本,平时不能在上面干活;

那在哪干活呢?干活都在dev分支上,也就是说,dev分支是不稳定的,到某个时候,比如1.0版本发布时,再把dev分支合并到master上,在master分支发布1.0版本;

你和你的小伙伴们每个人都在dev分支上干活,每个人都有自己的分支,时不时地往dev分支上合并就可以了。

所以,团队合作的分支看起来就像这样:

git-br-policy

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

Bug分支

stash可以把当前工作现场“储藏”起来,等以后恢复现场后继续工作:git stash

查看:git stash list

恢复:git stash apply && git stash drop git stash pop

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

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

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

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

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

Feature分支

git switch -c feature-vulcan
git add vulcan.py
git status
git commit -m "add feature vulcan"

git switch dev

# 强制删除 feature-vulcan
git branch -D feature-vulcan

如果要丢弃一个没有被合并过的分支,可以通过git branch -D 强行删除。

多人协作


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

  • 查看远程库的信息:git remotegit remote -v

  • 推送分支: git push origin master

    git push origin dev

推送:

  • master分支是主分支,因此要时刻与远程同步;
  • dev分支是开发分支,团队所有成员都需要在上面工作,所以也需要与远程同步;
  • bug分支只用于在本地修复bug,就没必要推到远程了,除非老板要看看你每周到底修复了几个bug;
  • feature分支是否推到远程,取决于你是否和你的小伙伴合作在上面开发。

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

$ git checkout -b dev origin/dev

碰巧你也对同样的文件作了修改,并试图推送:

$ cat env.txt
env

$ git add env.txt

$ git commit -m "add new env"
[dev 7bd91f1] add new env
 1 file changed, 1 insertion(+)
 create mode 100644 env.txt

$ 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  # 失败,原因是没有指定本地dev分支与远程origin/dev分支的链接,根据提示,设置dev和origin/dev的链接
git branch --set-upstream-to=origin/dev dev
git pull

# 解决冲突
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/

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

Rebase

标签管理

发布一个版本时,我们通常先在版本库中打一个标签(tag),这样,就唯一确定了打标签时刻的版本。按照tag v1.2查找commit就行

创建标签

  1. 切换需要标签的分支:git branch git checkout master

  2. 打标签:git tag v1.0

  3. 查看:git tag

  4. git tag v0.9 f7d08jjd

  5. 查看标签信息:git show <tagname>

  6. 还可以创建带有说明的标签,用-a指定标签名,-m指定说明文字:

    $ git tag -a v0.1 -m "version 0.1 released" 1094adb
    

操作标签

  • 删除标签: git tag -d v0.1

  • 推送标签到远程:git push origin v1.0

    一次性推送某个标签到远程:git push origin --tags

  • 删除远程标签:

    1. 删除本地标签:git tag -d v0.9
    2. 删除远程标签:git push origin :refs/tags/v0.9

使用Github,Gitee

自定义Git

.gitignore

忽略文件的原则是:

  1. 忽略操作系统自动生成的文件,比如缩略图等;
  2. 忽略编译生成的中间文件、可执行文件等,也就是如果一个文件是通过另一个文件自动生成的,那自动生成的文件就没必要放进版本库,比如Java编译产生的.class文件;
  3. 忽略你自己的带有敏感信息的配置文件,比如存放口令的配置文件。

git check-ignore命令检查.gitignore

配置别名

 git config --global alias.st status

Git Cheat Sheet

Git Cheat Sheet

Git

.gitnore	# 忽略文件列表
git init
	status
	add filename
	diff
	reset
	config
		--global user.name "XXXXX"
		--global user.email "XXXX"
	commit -m "XXXXXXX"
	rm --cached filename	# 不再追踪
	brach XXX			# add 分支
	checkout XXX	# 转换到某个分支
	merge	XXX 	#合并
	brach 	-d  XXX	
			-D XXX	# 强制删除
	remote add origin XXX #远程推送
	push --set-upstream 	
	log 	[--prettty=oneline]
	
# 回退
	|-----|--add--|-----|--commit--|-----|
time  1		2		3 		4			5
	
    1. 手动删除修改 
    	git checkout --file filename
    3.  git reset HEAD filename 
    	git checkout --file filename
	5. 回退版本
	
# 恢复
git checkout --filename

git remote add origin "address"
	push -u origin master
	push orign master
	

posted @ 2022-05-28 17:40  nsfoxer  阅读(21)  评论(0编辑  收藏  举报