版本控制工具 - Git
安装完成后,打开Git Bash,这是一个命令行工具,用于操作仓库和仓库的文件。你可以通过命令将已经存在的项目变成仓库,也可以重新创建一个新项目再通过命令将其变成仓库,还可以根据已经存在的仓库(Source))进行克隆。
Git相关的概念
版本库(version)
当仓库创建完成后,会出现一个名为.git的隐藏目录,该目录被称为版本库。
工作区(work)
仓库中的除了.git目录的其他任何目录都称为工作区。
暂存区(stage)
暂时存放向版本库添加的文件的区域,这个区域也在版本库中。你可以在工作区中创建文件或修改文件,然后通过git命令将新创建的文件或改动过的文件提交到暂存区,再通过git的commit命令将这些改动提交到master分支结构中,master分支结构是版本库中自动创建的一个区域,它以时间顺序保存了每一次改动过后的文件。工作区的所有文件都必须先add到暂存区,然后才能commit到master中。这个过程就是将文件提交到版本库以便可以被追踪监视,如果某个文件连一次都没有提交到暂存区或者分支结构中,那么在工作区中对该文件的任何修改都不会被监视到。
以下将工作区的两个文件add到暂存区
然后commit到master中,这样暂存区的文件会被清空。
分支(Branch)
我们提交的修改都提交到master分支结构中,但你也可以创建新的分支,比如当你提交一个只完成30%的文件源代码,这个源代码没有完成,但你提交到了远程仓库,当别人从远程仓库下载文件后,这个文件的代码因为不完整导致了不能正常运行,而这些源代码是由你编写的,别人不负责完善,所以造成了错误。针对这个问题,你可以创建一个分支结构,分支都是在本地创建的,远程永远不会出现你自己创建的本地分支,除非你将自定义分支推送到远程仓库。你对文件的修改都提交到分支中保存,别人看不到你的分支,也不可能从远程下载到你分支中的文件文件,这样就不会影响到别人,因为别人下载的文件是从远程master中下载的,这样你的分支就只属于你自己管理。当你完善了你负责的文件之后,再把你的分支与master分支结构合并起来即可。
为了便于理解,可以画图,master分支结构如图,每提交一次更改(commit),就会向右多出一个文件版本
使用git命令可以创建自己的分支结构,比如创建一个名为dev的分支,这个分支会先指向master,然后当你向dev提交修改时,它会向前增加文件版本,但这个操作只在分支上完成,不会进入master。当你在分支完成自己的任务后,可以使用git命令将分支合并到主结构master中。如图:
最后提醒:暂存区是一个公共空间,假如向master分支add了一个文件到暂存区,但尚未commit到分支结构中,那么这个文件在处于任何分支结构的工作区中都是可见的。
远程仓库(远程连接名称[默认是origin])
因为Git是分布式版本控制系统,所以我们可以搭建一个提供Git仓库服务的*服务器,不过你也可以直接使用GitHub网站提供的Git仓库服务,省去自己手动搭建服务器的开销。原始版本自然是存储在*服务器的Git仓库中,我们从远程下载这个仓库,然后修改文件再提交上传即可。
Git命令
创建仓库
cd:进入目录
pwd:显示当前所在目录,注意,使用git的操作文件的命令时必须先确保已经进入了即将要操作的文件的所在目录,这正是cd的作用,cd进入目录,再使用其他命令去操作文件。
git init:将当前目录转变为仓库,这会在当前目录生成一个.git的隐藏目录,它可以监视当前目录下的任何目录的改动,不要试图修改该目录的任何文件,否则Git仓库将失去作用。
还 可以直接在目录中右键,这样可以在当前目录打开git bash
配置账户
git config --global user.name "用户名" :设置仓库使用者的账户
git config --global user.email "用户名" :设置仓库使用者的电邮
git config user.name:显示账户
git config user.email:显示电邮
向版本库添加文件
将项目目录转变为仓库后,项目中的文件或新添加的文件都必须通过命令发送到master分支结构中存储。惟其如此,git才可以实现版本控制。以下在仓库目录中新建了一个txt文档
在工作区创建了新文件,还需要通过命令将新文件添加到暂存区,然后通过commit命令提交到master分支结构中。注意,add和commit并非必须连着使用,你可以在把文件add到暂存区后不使用commit,这样文件只存储在暂存区而不会进入版本库的master分支结构中。
git add fileName1 fileName2:将文件添加到暂存区
git commit -m"description":提交所有处于暂存区的文件到master中,即把暂存区的文件全部提交到版本库保存。提交时必须使用-m提交具有说明性的文字描述,也即,提交时顺便提交一个说明(-m""),以便于其他开发人员理解。
对比工作区和版本库的版本
git diff :比较工作区与暂存区的不同
git diff head -- fileName:比较工作区与分支版本库的不同
git diff --cached:比较暂存区与分支版本库的不同
两个区域的文件版本有不同时,此命令会输出提示信息,否则无显示。现在假设test.txt文件有这样一段字符:hello world sam,在文件字符末尾添加新字符welcom,接着add该文件到暂存区,接下来再次在test.txt中追加字符good boy,然后commit提交所有暂存区文件到版本库,最后使用diff命令会发现输出的对比中,绿色字体表示工作区的文件,该文件有good boy字符,而版本库中的文件却没有good boy字符。这是因commit只会提交处于暂存区的文件,而good boy的版本并未进入暂存区。
查看文件状态
git status:查看仓库中哪些文件被修改或删除
只有提交到版本库的文件才会被git监视,也只有被监视的文件才可以通过git status查看到文件的相关状态。如果一个文件未提交到版本库,那么对该文件做出的任何改动都不会被git发现。
现在修改test.txt,再hello world字符后添加新字符sam,然后保存并关闭文件。接着输入命令查看仓库状态。命令行会提示两条信息:
1.Changes not staged for commit:test.txt被改动过(modified)。如果忘记了哪些文件做过改动却未将改动提交到版本库,此时也可以使用git status命令进行查看,以便提交修改。提交一次改动就相当于是一次新的历史版本,今后想要回退到某个历史版本就需要从git中获取。注意,如果你对文件改动了N次,但都没有提交,则历史版本就不会有这个记录,直到提交后,这N次改动才会被当成一个历史版本。
2.Untracked files:仓库中的子目录:.vs目录、ConsoleApp目录、MyProgram目录下的文件未被监视,请使用git add 和git commit命令将这些文件添加到版本库。
保存文件改动
文件改动后需要提交修改到版本库,使用的命令与添加文件的命令是一样的(git add和git commit命令)。
查看改动的历史版本
git log:查看改动历史
git log --pretty=oneline :查看改动历史(简洁版)
查看改动的历史版本的head头
每个版本都有一个基于SHA1算法的head头,用来表示版本的唯一性。
git reflog:查看历史版本的head头信息
时光穿梭
git reset --hard headCode:将文件重置为指定的版本
通过git reflog得到历史版本的头信息后,再使用git reset --hard headCode将文件重置为指定版本
撤回暂存区文件
git reset HEAD fileName:将暂存区中的指定文件撤回,即删掉。
将工作区文件替换为版本库中的最新版本
git checkout -- fileName:修改或删除某个文件,但还未更新提交到版本库(暂存区或master分支结构中),则可以使用此命令将文件替换为该文件进入版本库中的最近一次提交的版本
比如修改了a.txt,添加了hello字符,但这个修改并未提交到版本库,此时想要还原到上一次提交到版本库的版本,可使用checkout,命令完成后,a.txt会恢复到没有hello字符的版本。
删除工作区文件
rm fileName:删除工作区文件,但不会删除版本库中的文件
也即,删除后还可以通过checkout将版本库中的最近一次提交的版本还原到工作区。如果rm后再commit,会提示该文件的删除操作不能提交到版本库,也即commit是无效的
同时删除工作区和版本库中的文件
git rm fileName:删除工作区的文件,但还是可以通过reset还原该文件。
git commit -m"description":提交删除操作到当前分支,删除记录会存档
创建远程仓库
第一步:注册Github账户
第二步:打开git bash,再命令行中键入ssh-keygen -t rsa -C "'你注册GitHub账户锁使用的电邮地址'" (注意此处的电邮地址需要用一对双引号包含一对单引号,单引号包含电邮地址才可以,否则会提示too many arguments),这个命令会根据你提供的电邮生成一个私匙和一个公匙,用于你向GitHub传输加密数据时所提供的身份凭据。
第三步:命令行会提示你按Enter键,每按一次Enter则会创建相应的文件,不用管,一路Enter到底直到出现下图的提示即完成
第四步:打开本地目录C:\Users\你的windows账户名\,看到.ssh目录,打开.ssh目录后看到下面两个钥匙文件则说明上述步骤已经成功
第五步:登录GitHub网站,点击头像 - Settingss - 进入设置界面,如下:
第六步:打开.ssh目录中的id_rsa.pub文件,将公匙copy一份粘贴到SSH Key中:
添加完成后,如图:
你可以添加多个SSH Key,你自己所使用的每一台PC都可以创建这样的钥匙以供你在多台PC上连接远程仓库。连接远程仓库需要使用Git命令,该命令会导致GitHub网站读取你本地的SSH Key,将这个Key与Git远程仓库中注册的Key进行匹配,如果远程仓库有这个Key,你才可以推送数据到远程仓库中。
创建远程仓库
点击头像旁边的+号 - new Repositories
你可以将本地仓库推送到你在GitHub创建的远程仓库中,使两个仓库合并为一个,也可以把远程仓库克隆到本地。
删除远程仓库
进入settings - Repositories - 点击你的仓库进入仓库页面 - setting - 拉到底 - delete Repository
创建与本地关联的远程仓库连接
git remote add 远程连接名称 git@github.com:Gihub账户名/仓库名.git:创建远程连接,在git bash中应先cd到与远程仓库目录相同的本地仓库目录时此命令才有效
远程连接名称默认origin,你可以自定义,如:
git remote add GitHub git@github.com:Gihub账户名/仓库名.git
删除远程仓库连接
git remote rm 远程连接名称[默认是origin]
查看远程仓库连接
git remote -v:查看当前与本地关联的远程连接
抓取远程仓库到本地仓库
git pull --rebase 远程连接名称[默认是origin] master:远程仓库合并到本地仓库,必须先执行此命令,然后才可以使用其他命令将本地仓库的文件推送到远程仓库。
如果输入此命令后提示以下错误:
fatal: Could not read from remote repository.
Please make sure you have the correct access rights
and the repository exists.
解决办法:
git remote set-url 远程连接名称[默认是origin] git@github.com:Gihub账户名/仓库名.git
推送本地分支到远程仓库
git push -u 远程连接名称[默认是origin]] 分支名:推送到远程的指定分支,加-u表示默认推送到此分支,下次只需要使用git push命令而不需要再次提供分支名就可以继续往该分支推送数据
git push 远程连接名称[默认是origin] 分支名:推送到远程的指定分支
将本地自定义分支推送到远程仓库
下面例子中远程仓库还没有创建coldfood分支,但你在本地可以创建它,然后把该分支推送到Github。这样做是因为想要将coldfood共享于多个开发人员,否则也没必要把本地分支推送到Github。
从远程仓库克隆自定义分支
git checkout -b dev 远程连接名称[默认是origin]/dev:将远程仓库的自定义分支克隆到本地仓库
如果另一个开发人员要从远程克隆某个我们所共享的Git仓库,而git远程仓库并不是由他创建,现在假设仓库里有自定义的coldfood分支,那么他克隆到本地后默认是没有coldfood分支结构的,可以使用git branch查看,命令行只会显示master分支。
建立本地分支和远程分支链接
git branch --set-upstream 本地分支名 远程连接名称[默认是origin]/远程分支名:将远程的自定义分支与本地自定义分支关联起来
如果另一个开发人员要从远程克隆某个我们所共享的Git仓库,而git远程仓库并不是由他创建,他克隆完分支之后,将修改的文件往分支提交,接着尝试推送到远程仓库对应的分支上,那么这会失败,因为还没有将该分支与远程关联起来。
远程推送冲突
git pull 远程连接名[默认是origin]:抓取远程的文件
当两个人都修改了同一个文件,A先推送到远程仓库,然后B再推送,那么B的推送会失败,因为远程会尝试merge,此时会出现合并冲突。解决办法是B应该先从抓取远程该文件,这个文件时A修改后的提交,然后在本地手动解决冲突(打开抓取的文件,将B的修改追加或替换到A修改的文件中)后再推送。
Auto-merging 1.txt //提示:1.txt存在冲突,远程的1.txt时最新版本,你需要手动修改冲突
CONFLICT (content): Merge conflict in hello.py
Automatic merge failed; fix conflicts and then commit the result.
克隆远程仓库到本地
git clone git@github.com:Gihub账户名/仓库名.git:将远程仓库克隆到本地,在git bash中应cd到本地某个目录时此命令才有效
克隆的仓库存放在本地x目录,则需要先cd到x目录后再使用clone命令,比如远程仓库MyProgram需要克隆到本地的yin目录中:
创建分支
git branch 分支名称:创建自定义的分支结构
查看所有分支
git branch:列出所有分支
绿色表示当前工作区所使用的分支
切换分支
git checkout 分支名称:从其他分支结构切换到指定的分支结构中
切换之后,add、commit等操作都会指向当前分支。
删除分支
git branch -d 分支名称:删除指定的空分支,删除前必须先切换到其他分支中,比如切换到master中,之后才可以删除指定分支
git branch -D 分支名称:强行删除非空的分支,如果分支存在文件则删除时会提醒尚未merge,即默认情况下任何分支中存在文件版本则必须将其merge合并到主分支master中,此命令无视此规则,强行删除。
合并分支
git merge 分支名称:将指定分支合并到master中,合并后,指定分支继续存在,你还可以向分支提交修改。必须切换到master后才能执行此命令。
假设工作区有一个test.txt文件,文件中有hello world字符,该文件默认是提交到master分支结构中。如果后来你创建了自己的分支,比如创建了一个名为coldfood的分支,然后对test.txt文件追加了一个字符sam,接着提交修改到coldfood,那么当checkout到master后,工作区的test.txt文件会变成在master分支结构中的版本,也即打开文件后看不到字符sam。此时使用merge对分支进行合并,合并后,分支中的最新版本的文件就会替换掉在master中的最新版本。也即test.txt文件末尾会出现字符sam。
分支与工作区的关系
1.工作区中任何还没有首次提交到版本库的非远程仓库下载的文件不属于任何分支(包括master),不管切换到哪个分支能在工作区看到这些文件
23.将Github远程仓库合并到本地仓库后,工作区中所有的文件默认处于暂存区中,也即切换到任何分支都能在工作区看到这些文件
3.如果一个文件在每个分支中都有提交过,那么将分支merge时将会发生合并冲突,需要手动解决冲突,在这种情况下merge时,会提示类似下面的信息:
CONFLICT (content): Merge conflict in readme.txt //合并发生了冲突
Automatic merge failed; fix conflicts and then commit the result. //合并 错误,请手动修复冲突,再commit修改
merge冲突发生后,git会将文件进行修改,打开文件可以看到下面的信息,它显示了在两个不同分支上的相同文件的数据,HEAD是提交到master分支结构的数据,而coldfood是提交到coldfood分支的数据。现在只需要修改该文件为你想要提交的数据然后在master上add、commit即可。
修改为:
合并分支模式
合并分支模式是指合并分支并手动删除分支之后,是否保存合并分支时的历史记录。
Fast forward模式
git merge 分支名称
这是默认merge时git使用的合并模式(快速合并),当分支被手动删除后,git log将不会保存合并分支时的历史记录
禁用Fast forward模式
git merge --no-ff -m "description" 分支名称
使用此模式,删除分支后,git log仍然会保存合并分支时的历史记录
Bug分支
git stash:将暂存区的文件隐藏
git stash pop:删除隐藏文件并将文件显示在暂存区中
git stash list:查看隐藏的文件列表
良好的习惯是每次修改完文件都commit到分支结构中,我们知道暂存区是公共区域,不管你切换到哪个分支,在工作区你都可以看到该文件。如果你只是把文件add到暂存区,那么该文件在处于任何分支的工作区中都将是可见的,这很容易引起迷惑,因为下一次你打开工作区看到该文件时,你可能已经不知道它是属于哪个分支中的文件。而假如今天你的代码并没有写完,所以你并不想commit,此时你可能想先提交到暂存区保存起来,如果你这样做,那么在处于任何分支的工作区中都能看到这个文件,而你不想看到它,觉得胀眼?那就只能使用stash命令将暂存区的文件隐藏起来。以下在coldfood分支中隐藏了一个3.txt的文件,再通过stash pop删除隐藏文件并显示在暂存区中。
版本号
版本号默认情况下不会随推送进入远程仓库,仅本地有效。
git tag:查看版本号列表
git show 版本号:查看指定版本的详细信息
git tag tagName:为当前分支中的最新提交的文件版本打版本号
git tag tagName commitHeadCode:为指定了head头信息的commit打版本号
git tag -a tagName -m"description" commitHeadCode:为指定了head头信息的commit打版本号,同时指定了描述
虽然可以获取某个commit的记录,但是用版本号来标识版本更简单,毕竟要得到一个commit的记录需要提供head,而head只是一堆6a5819e...,而版本号可能是这样v1.1。切换到某个分支,再使用此命令即可。虽然可以为每一次的commit都打一个版本号,但你可能在上一次commit时忘记了打版本号,通过查看git log找到head,补上版本号即可:
6a5819e merged bug fix 101 //6a5819e是head头
cc17032 fix bug 101
7825a50 merge with no-ff
6224937 add merge
59bc1cb conflict fixed
400b400 & simple
75a857c AND simple
fec145a branch test
d17efd8 remove test.txt
git tag v0.9 6a5819e
删除版本号
git tag -d 版本号:删除指定版本号,并不删除文件
将版本号推送到远程
git push 远程连接名称[默认是origin] 版本号:推送指定的本地版本号到远程仓库
git push 远程连接名称[默认是origin] --tags:推送本地所有版本号到远程仓库
删除远程版本号
$ git push 远程连接名称[默认是origin] :refs/tags/版本号:先要在本地删除指定的版本号,之后才能使用此命令
git push 远程连接名称[默认是origin] --delete tag 版本号:同上
添加例外
有些文件并不需要添加到仓库,比如windows自带的Thumbs.db、ehthumbs.db、Desktop.ini、编译生成的中间文件,但当你输入git status时,Git
会提醒你Untracked files(文件未被跟踪),如果有洁癖,可以使用.gitignore文件定义文件例外。这使用例外之后,status会忽略未被跟踪的文件。
在windows中打开记事本,编辑如下信息,然后另存为.gitignore。
Thumbs.db
ehthumbs.db
Desktop.ini
如果某些文件已经被纳入了Git,则修改.gitignore是无效的。解决方法就是先把本地缓存删除(改变成未track状态),然后再提交。
git add .
git commit -m 'update .gitignore'
最后将.gitignore文件commit一下就可以了。
GitHub开源仓库
你可以对GitHub网站上的任何一个开源的仓库进行Fork,将仓库Fork到自己的GitHub账户上,然后通过git命令克隆到本地。
建议提交
如果你对我的源码感兴趣,你肯定想搞到本地加以研究,此时你需要将开源的仓库fork到你的GitHub上,然后再clone到本地。然后你发现源码可以继续优化,所以你花了一个月修改源码,接着你肯定想通知我,此时你使用Git命令pull request向我发起建议提交的申请,如果我觉得你改得不错,我就接受你的修改,此时我会将你的提交merge到我的仓库中。了解pull request可以移步至:创建 Pull Request
使用码云
也可以使用码云托管你的源代码,注册完成后点击头像 - 设置 - SSH Key - 添加公匙,操作与GitHub是一样的:https://gitee.com
本地同时关联GitHub和码云
git remote add github git@github.com:xxx/zzz.git //关联到GitHub仓库
git remote add gitee git@gitee.com:xxx/zzz.git //关联到码云仓库
git remote -v //查看当前与本地关联的远程Git仓库连接
git pull github //抓取github
git pull gitee //抓取码云
git push github master //推送到github
git push gitee master //推送到码云