赞
踩
$ git config --global user.name "Your Name"
$ git config --global user.email "email@example.com"
$ git init
//把某个目录变成Git可以管理的仓库
$ git add <文件名>
//用命令git add告诉Git,把文件添加到仓库Git添加
文件需要add,commit一共两步呢?因为commit可以一次提交很多文件,所以你可以多次add不同的文件,比如:
$ git add file1.txt
$ git add file2.txt file3.txt
$ git commit -m "add 3 files."
git add *
添加所有修改文件到暂存区
git add <filename>
添加某个文件到暂存区
git commit -m "自定义记录"
提交暂存区的修改到版本库
第一步是用git add
把文件添加进去,实际上就是把文件修改添加到暂存区;
第二步是用git commit
提交更改,实际上就是把暂存区的所有内容提交到当前分支。
实战集锦
如果commit
git status
命令可以让我们时刻掌握仓库当前的状态Untracked
这三个分别是:
第一个是add之后但是没有commit的
第二个是没有add的
第三个是新文件,未被追踪的
git push
:更新本地git版本库到remote版本库:git push origin master
:将本地的master分支推送到origin
主机的master
分支。如果master
不存在,则会被新建。git push origin master --force
:表示将本地版本库强制推到远程版本库,该操作比较危险,如果不是很有把握,一定不要这么做git diff
这里显示的是本地没有提交的和提交的版本上的差异,显示的格式正是Unix通用的diff格式
git log
命令显示从最近到最远的提交日志
git log --pretty=oneline
显示一行
git log --graph
分支合并图
git log --pretty=oneline
分支合并图 每个一行显示
git reset --hard HEAD^
首先,Git必须知道当前版本是哪个版本,在Git中,用HEAD表示当前版本,也就是最新的提交3628164…882e1e0(注意我的提交ID和你的肯定不一样),上一个版本就是HEAD,上上一个版本就是HEAD,当然往上100个版本写100个比较容易数不过来,所以写成HEAD~100。
命令执行后会提醒:
Unstaged changes after reset:
M README.md
这里直接把head指针修改了,但是文件的内容没有修改,需要借助1.7小节命令将其工作区的文本文件还原(具体原理见1.6),通过git status可查看,直接checkout就可以将文本变为想还原的版本了
后悔了,还想找到最新的怎么办呢?
办法其实还是有的,只要上面的命令行窗口还没有被关掉,你就可以顺着往上找啊找啊,找到那个append GPL的commit id是3628164…,于是就可以指定回到未来的某个版本:
git reset --hard 3628164
Git的版本回退速度非常快,因为Git在内部有个指向当前版本的HEAD指针,当你回退版本的时候,Git仅仅是把HEAD从指向append GPL:
改为指向add distributed:
然后顺便把工作区的文件更新了。所以你让HEAD指向哪个版本号,你就把当前版本定位在哪。
想恢复到新版本怎么办?找不到新版本的commit id怎么办?
在Git中,总是有后悔药可以吃的。当你用$ git reset --hard HEAD^回退到add distributed版本时,再想恢复到append GPL,就必须找到append GPL的commit id。Git提供了一个命令git reflog用来记录你的每一次命令
$ git reflog
a4a4527 (HEAD -> master, origin/master) HEAD@{0}: reset: moving to a4a45
dae796b HEAD@{1}: reset: moving to HEAD^
a4a4527 (HEAD -> master, origin/master) HEAD@{2}: commit: the third times modify readme.md
dae796b HEAD@{3}: commit: modify readme.md & add test2.txt
bdacecc HEAD@{4}: commit: new file readme.md
76b4650 HEAD@{5}: commit (initial): my first test
注意这里前面的那个id就是后面命令执行后最新的版本号,所以就可以回到最新的了
github怎么提交回退旧版本代码并更改后的文件到主分支上? (原文链接)
可能说的不是很明白,就是我代码写着写着,发现我已经不想这么弄了,用git reset --hard <版本号>退回到之前的某个版本重新写,这样当我当我写完之后,想在提交到远程仓库,它就会报错:
To https://github.com/zifenggao/wenda.git
! [rejected] master -> master (non-fast-forward)
error: failed to push some refs to ‘https://github.com/zifenggao/wenda.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 reset --hard
,那就可以推断出你已经add
和commit
过了。
其次,根据报错,可以推断出你已经push
过了(这个推断基于只有你一个人拥有master branch
的更改权限。
那么当你执行git reset --hard
之后,历史纪录是不能跟远程的记录直接合并的。因此才会有这个报错。
举个例子,远程是A -> B -> C -> D
,你git reset --hard
之后是A -> B
。这时候除非远程那边抹掉 C 和 D,否则是不能合并的。
因此,这时候,你应该使用git push origin master --force
来强行覆盖远程记录。
请不要根据提示使用git pull
。否则,你的本地又会变成A -> B -> C -> D
。因为git pull
相当于git fetch + git merge
(以下内容基于上面的例子,远程是A -> B -> C -> D
,你想回滚到 B 那个状态)
楼上提到了git revert
。其实,git reset --hard
和git revert
都可以实现“回滚代码”。但区别在于:
git revert
会把你的本地变成A -> B -> C -> D -> E
。其中,E 干的事儿是删除 C 和 D。这样做的好处在于,你git push origin master
就不会有上面的报错了。但,历史线上还是会保留 C 和 D 这两个commit
。如果使用这个命令,记得要add
然后commit
。
git reset --hard
会直接删掉 C 和 D,形成 A -> B 这样的结果。好处在于更直接更彻底。缺点在于,首先要通过git push origin master --force
去强行更改。其次,一旦你后悔了,除非根据本地的 reflog 直接恢复 HEAD 指针,此外没有其他办法。
用哪个都是没错的,请根据自己的需要来选择。
命令git checkout -- readme.txt
意思就是,把readme.txt文件在工作区的修改全部撤销,这里有两种情况:
总之,就是让这个文件回到最近一次git commit
或git add
时的状态。
使用场景:
场景1:当你改乱了工作区某个文件的内容,想直接丢弃工作区的修改时,用命令git checkout – file。
场景2:当你不但改乱了工作区某个文件的内容,还添加到了暂存区时,想丢弃修改,分两步,第一步用命令git reset HEAD file,就回到了场景1,第二步按场景1操作。
场景3:已经提交了不合适的修改到版本库时,想要撤销本次提交,参考版本回退一节,不过前提是没有推送到远程库。
删除文件:
$ git rm test.txt
$ git commit -m "remove test.txt"
查看remote名称,增加-v可查看详细信息
廖雪峰“说”:
分支在实际中有什么用呢?假设你准备开发一个新功能,但是需要两周才能完成,第一周你写了50%的代码,如果立刻提交,由于代码还没写完,不完整的代码库会导致别人不能干活了。如果等代码全部写完再一次提交,又存在丢失每天进度的巨大风险。
现在有了分支,就不用怕了。你创建了一个属于你自己的分支,别人看不到,还继续在原来的分支上正常工作,而你在自己的分支上干活,想提交就提交,直到开发完毕后,再一次性合并到原来的分支上,这样,既安全,又不影响别人工作。
其他版本控制系统如SVN等都有分支管理,但是用过之后你会发现,这些版本控制系统创建和切换分支比蜗牛还慢,简直让人无法忍受,结果分支功能成了摆设,大家都不去用。
但Git的分支是与众不同的,无论创建、切换和删除分支,Git在1秒钟之内就能完成!无论你的版本库是1个文件还是1万个文件。
每次提交,Git都把它们串成一条时间线,这条时间线就是一个分支。截止到目前,只有一条时间线,在Git里,这个分支叫主分支,即master分支。HEAD严格来说不是指向提交,而是指向master,master才是指向提交的,所以,HEAD指向的就是当前分支。
一开始的时候,master分支是一条线,Git用master指向最新的提交,再用HEAD指向master,就能确定当前分支,以及当前分支的提交点
而增加分支就相当于增加新的dev指针(详情)
-b参数表示创建并切换 :$git checkout -b dev
,上面相当于:git branch dev
和git checkout dev
回到master分支: $ git checkout master
切换分支:git checkout <name>
创建+切换分支:git checkout -b <name>
git branch
命令会列出所有分支,当前分支前面会标一个*号。
git branch <name>
创建分支
git branch -d <name>
删除分支
git merge <name> --no-ff -m "merge信息"
合并某分支到当前分支 --no-ff
表示禁用fast forward
更多分支相关的问题参见Git 分支 - 分支的新建与合并
关于fast forward: Git- Fast Forward和no fast forward
git pull
相当于git fetch
+git merge
git push --set-upstream origin <分支名称>
远程创建分支,不过这里需要在本地存在已经创建的分支,说白了这个命令是将本地的分支push到远程版本库
正常情况下,我们可能不会显式的创建分支,但是在merge的时候其实实质上还是会用到分支的知识,下面介绍几种情形。
我们的代码从远程版本库clone之后,在我们修改的同时,别人已经先人一步提交他们的代码,这样我们再提交的时候,就会出现两种情况:
我们修改的文件别人没有修改过,这个时候我们就可以通过两种方式进行merge:
(当然对于这种情况还是推荐用第一种方式,因为用第二种方式的时候会多出一个版本来,多多少少有点乱)
git pull
命令把远程代码同步下来,然后再用git add
和git commit
命令来更新你的代码,git pull
的效果图:git add
和git commit
命令在本地创建版本,然后再git pull,这个时候因为出现了版本冲突,会在pull的时候增加一个版本,就是merge的版本,如果没有什么文件上的冲突,在弹出的merge log里面添加日志,就可以合并到最新版本了。下面是详细的过程(左面和右面的一栏开始的内容是一样的,然后先增加text.txt,然后提交到远程版本库,第二个在增加text2.txt,最后提交时需要解决版本冲突~)我们修改的文件别人已经修改过,提交到了远程仓库,这个时候再使用git pull
就会报下面的错误了:
所以说上面的第一种方法失效,针对这种情况,我们只能通过一种方式进行解决了,流程和上面一种情况的方法二一样,只不过因为存在文件冲突,需要手动解决掉冲突然后在push:
先用git add
和git commit
命令在本地创建版本,然后再git pull,对于出现文本冲突的文档,手动选择修改,然后填写好merge日志之后就可以再次push了。详情如下:
这里大家会看到冲突的文件发生了变化,下面详细说下冲突文件出现变化的语法现象:
上图中被红框标记的部分,前两个红框之间的部分,是本地的内容。
后两个红框之间的部分,是origin/master远端的内容。怎么修改决定于你。但是最后要把红框红的内容删除
综上,我们再做工程的时候,因为不知道本地修改的代码是在哪个基础上修改的,所以在提交的时候先git pull 一下:
git pull
自动就merge了)Git有commit,为什么还要引入tag?
“请把上周一的那个版本打包发布,commit号是6a5819e…”
“一串乱七八糟的数字不好找!”
如果换一个办法:
“请把上周一的那个版本打包发布,版本号是v1.2”
tag应运而生!!!
git tag v1.0 //默认标签是打在最新提交的commit上的。
git tag -a <tagname> -m "blablabla..."//可以指定标签信息;
git tag v0.9 6224937 //提交打标签,对应的commit id是6224937
git tag //查看所有标签
git show v0.9 //查看标签信息
git tag -d v0.1 //删除标签
git push origin v1.0 //推送本地v1.0标签到远程
git push origin --tags //推送本地所有标签到远程
/*远程删除tag,先在本地删除,再删除远程(好像直接远程也可以吧)*/
git tag -d v0.9
git push origin :refs/tags/v0.9
绑定自己的账号和公开自己起的名字
$ git config --global user.name "Your Name"
$ git config --global user.email "email@example.com"
Git显示颜色,会让命令输出看起来更醒目
$ git config --global color.ui true
echo "# sss" >> README.md
git init
git add README.md
git commit -m "first commit"
git remote add origin https://github.com/zsweet/xxx.git
git push -u origin master
添加后,远程库的名字就是origin,这是Git默认的叫法,也可以改成别的,但是origin这个名字一看就知道是远程库。
由于远程库是空的,我们第一次推送master分支时,加上了-u参数,Git不但会把本地的master分支内容推送的远程新的master分支,还会把本地的master分支和远程的master分支关联起来,在以后的推送或者拉取时就可以简化命令。
从现在起,只要本地作了提交,就可以通过命令:
$ git push origin master
//或者直接 git push
git remote add origin https://github.com/zsweet/xxx.git
git push -u origin master
克隆远程git到本地,两种方式都可以:我常用第二个
$ git clone git@github.com:michaelliao/gitskills.git
$ git clone https://github.com/michaelliao/gitskills.git
参考:
彻底删除git中没用的大文件
寻找并删除Git记录中的大文件
rebase的作用简要概括为:可以对某一段线性提交历史进行编辑、删除、复制、粘贴;因此,合理使用rebase命令可以使我们的提交历史干净、简洁!
蜗牛说
- 其实在实际工作场景中,尤其是一些大厂或者好多人维护的项目,每一次push提交后合入主分支都需要管理员Code Review(简称CR),如果同时有多个commit id是无法上传的也不方便cr的,因此通过rebase命令,本地的一些提交就可以很随意,等push的时候再整理一下就可以了,可以大大提高开发的效率。
- Git Merge vs Git Rebase: 更多详情[参考]
相同点:git merge 和 git rebase 都是非常有用的命令
不同点:1. 每当运行 git merge 时,都会创建一个额外的合并提交。每当你在本地仓库中工作时,合并提交过多会使提交历史看起来很混乱。避免合并提交的一种方法是改用 git rebase. 2. git rebase 是一个非常强大的功能,但是如果没有以正确的方式使用它也是有风险的。 git rebase 会更改提交历史记录,因此请谨慎使用。如果 rebase 是在远程仓库中完成的,那么当其他开发人员尝试从远程仓库中提取最新的代码更改时,可能会产生很多问题。请记住仅在本地仓库中运行 git rebase。
rebase常见的有如下几种使用场景:
场景1. 合并多个commit为一个完整commit
当我们在本地仓库中提交了多次,在我们把本地提交push到公共仓库中之前,为了让提交记录更简洁明了,我们希望把如下分支B、C、D三个提交记录合并为一个完整的提交,然后再push到公共仓库。
现在我们在测试分支上添加了四次提交,我们的目标是把最后三个提交合并为一个提交:
这里我们使用命令:
git rebase -i [startpoint] [endpoint]
其中-i
的意思是--interactive
,即弹出交互式的界面让用户编辑完成合并操作,[startpoint]
[endpoint]
则指定了一个编辑区间,如果不指定[endpoint]
,则该区间的终点默认是当前分支HEAD
所指向的commit
(注:该区间指定的是一个前开后闭的区间)。
在查看到了log日志后,我们运行以下命令:
git rebase -i 36224db
或
git rebase -i HEAD~3
然后我们会看到如下界面:
上面未被注释的部分列出的是我们本次rebase操作包含的所有提交,下面注释部分是git为我们提供的命令说明。每一个commit id 前面的pick表示指令类型,git 为我们提供了以下几个命令:
pick:保留该commit(缩写:p)
reword:保留该commit,但我需要修改该commit的注释(缩写:r)
edit:保留该commit, 但我要停下来修改该提交(不仅仅修改注释)(缩写:e)
squash:将该commit和前一个commit合并(缩写:s)
fixup:将该commit和前一个commit合并,但我不要保留该提交的注释信息(缩写:f)
exec:执行shell命令(缩写:x)
drop:我要丢弃该commit(缩写:d)
根据我们的需求,我们将commit内容编辑如下:
然后是注释修改界面:
编辑完保存即可完成commit的合并了:
场景2. 将某一段commit粘贴到另一个分支上
当我们项目中存在多个分支,有时候我们需要将某一个分支中的一段提交同时应用到其他分支中,就像下图:
我们希望将develop分支中的C~E部分复制到master分支中,这时我们就可以通过rebase命令来实现(如果只是复制某一两个提交到其他分支,建议使用更简单的命令:git cherry-pick)。
在实际模拟中,我们创建了master和develop两个分支:
master分支:
develop分支:
我们使用命令的形式为:
git rebase [startpoint] [endpoint] --onto [branchName]
其中,[startpoint] ``[endpoint]
仍然和上一个命令一样指定了一个编辑区间(前开后闭),--onto
的意思是要将该指定的提交复制到哪个分支上。
所以,在找到C(90bc0045b)和E(5de0da9f2)的提交id后,我们运行以下命令:
git rebase 90bc0045b^ 5de0da9f2 --onto master
注:因为[startpoint] [endpoint]指定的是一个前开后闭的区间,为了让这个区间包含C提交,我们将区间起始点向后退了一步。
运行完成后查看当前分支的日志:
可以看到,C~E部分的提交内容已经复制到了G的后面了,大功告成?NO!我们看一下当前分支的状态:
当前HEAD处于游离状态,实际上,此时所有分支的状态应该是这样:
所以,虽然此时HEAD所指向的内容正是我们所需要的,但是master分支是没有任何变化的,git只是将C~E部分的提交内容复制一份粘贴到了master所指向的提交后面,我们需要做的就是将master所指向的提交id设置为当前HEAD所指向的提交id就可以了,即:
git checkout master
git reset --hard 0c72e64
此时我们才大功告成!
场景3. 本地开发基于的版本落后于或者不是远程分支最新版本,避免push出现新的commit id
多人使用同一个远程分支合作开发,如果远程分支有新的 commit 未同步到本地,而自己的本地开发又有新的commit,在 push 代码的时候很可能出现以下问题,导致无法推送:
最简便的方法是直接采用git pull origin master
从远程拉取并进行merge(git pull = git fetch & git merge
),但是这样就会因为merge而多出来一个merge的commit节点,默认是这样的:
如果多人多次如此操作,那么提交记录就会出现很多条这种自动生成的 merge commit,非常难看。甚至在一些需要cr的场景中,这种操作也不太被允许和推荐。
那这就是需要运用rebase进行拉取,就不会产生merge的节点了。
git pull --rebase origin master
执行 git pull --rebase 的时候必须保持本地目录干净。即:不能存在状态为 modified 的文件。(存在Untracked files是没关系的)
如果出现冲突,可以选择手动解决冲突后继续 rebase,也可以放弃本次 rebase。具体操作细节可参考:git pull --rebase
# 可选方式1: 手动解决 + 继续rebase。 手动解决冲突后
$ git add one.md
$ git rebase --continue
# 可选方式2: 放弃本次rebase
git rebase --continue
蜗牛说:
上面其实说的是将自己分支上跟远程分支rebase,但是我平时一般不直接跟远程分支rebase,而是喜欢新创建一个开发分支,需要rebase的时候,流程如下:
step1 : 从开发分支切换到本地master分支(git checkout master)
step2: 在master分支上git pull(因为本地master分支是干净的,不会出现merge节点)
step3: 切回开发分支并rebase:git checkout branch && git rebase master
更多git rebase可以参考官方文档:git-rebase
参考资料
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。