沉淀,再出发:Git的再次思考
沉淀,再出发:Git的再次思考
一、前言
使用git也有很久了,后来有一段时间一直没有机会去使用,现在想来总结一下自己学习了这么长时间的一些心得感悟,我写的博客一般都是开了一个轮廓和框架,等到以后有所思有所悟了还会增加的,正如软件一样一直都会不断地更新,加入新的东西,修改一些过时的东西,我们做人和做事也应该这样,不断地学习新的知识,取其精华去其糟粕,不断地进步和增长。关于git也是用来进行项目管理的,同样是为了团队协作,这个场景使大家使用着各自的电脑,可能相隔万里,只要能够进行通信,就能够从github上进行代码的同步操作,这是非常有意思的。
相比于以前的subversion等项目管理软件,git更加的强大,并且很好用,这里的好用有两种意思,第一是功能很强大,每一个成员都可能将中心仓库的代码给删除了,当然也能植入自己的内容,第二个好用是一不小心提交了错误的代码同样的也会被其他成员所使用,造成很恶劣的影响。因此十分熟悉git的操作,明白背后的意义,以及合理的使用git来进行代码的维护是非常重要的。上次我们学习了maven是为了管理依赖,统一风格,而git则是为了同步代码,管理项目的进程,这两者一个面向于代码的同步和转移,另一个面向于项目内部的实现和依赖的处理,两者结合起来组成的开发能力就非常的强大了,对于团队开发来说非常的重要,是两把利剑。
二、Git的基本操作
2.1、Git和Github
众所周知,Git是一个软件,安装在本地的机器上,用来从Github这个远程仓库中pull、push代码,从而达成团队协作开发的目的。这两者就好比是抽水机和池塘,Git从GitHub之中得到代码并且维护着版本和分支的信息,从而进行合并和开发。因此使用Git必须要有Github账号以及项目才行。
2.2、安装Git
Git的安装非常简单,只需要从官网上下载并安装就可以了,不用配置什么环境变量就能够进行使用了。同样的官网上还提供了很多的GUI用来对Git获得的代码进行分析和操作,我们可以大致了解一下。安装的过程很简单,我们不再赘述,安装完成之后就能打开使用了。可以看到这个shell使用的是Linux中的命令,非常的有意思。
2.3、创建Github项目并clone到本地
接下来我们在github上创建一个项目,放一些文件进去便于我们的实验。考虑到多人协作的问题,我们先创建一个组织,在组织下面在创建项目,同时可以邀请其他人加入我们的项目实现协同工作。
这样我们的项目就建好了,剩下的就是邀请其他人进入其中,并且最好的就是将别人的公钥放入自己的Github上去,这样其他人在使用Git的时候就不用每次都输入账户和密码了。在设置,SSH或者GPG秘钥之中,我们加入自己和别人的公钥。但是需要注意的一点就是,我们如果使用SSH加密的话,需要将项目改成使用SSH能够获取的,之后我们在添加SSH秘钥,这一点非常重要!!!!!!
在Git中使用如下命令来生成秘钥和公钥:
ssh-keygen -t rsa -C 邮箱号
然后我们转到生成的公钥和私钥的目录,私钥是我们自己的打死都不能给别人,而公钥就是让别人拿来给自己进行通信的,因此,我们把公钥发给这个项目的管理员,让他在刚刚的地方复制进去我们的公钥,这样就能很好的工作了。
我们可以根据Git项目的网址将项目clone到本地,这个不需要秘钥,但是如果以后提交内容的时候就需要了:
比如我们在这个时候不加入公钥到仓库之中,那么我们操作到了git push的时候就不能向下执行了,非常的麻烦,需要输入账号和密码,并且之后还是需要配置的。
那么我们将公钥加入仓库之中,此时管理员会收到邮件提醒,做的还是很到位的:
然后我们再重新下载一下项目,可以看到项目加载成功!
2.4、开始进行开发和提交
对于clone下来的项目,开发就是按照以往的开发方法,将这些文件导入到myeclipse之中,可能使用了maven,那就更新依赖,之后就可以进行开发了,那么什么叫做开发呢?!开发的本质其实就是增加文件、修改已有的文件、删除某些文件,再说白一点就是对原来的文件库进行增删改操作,这就是开发,那么因为这些操作必然会影响到我们的本地仓库之中的代码,比如我们增加了一个文件,又修改了一个文件,还删除了一个文件,然后要怎么样同步到远程仓库之中呢,那就需要用到Git的强大功能了,另外提醒一点,在我们使用git的时候一定要把myeclipse关闭了,不然因为其中某些文件没保存,或者产生了一些临时文件被我们提交了就可能造成错误。
下面我们模仿开发之后提交的过程:
首先,当我们clone下来文件之后,第一件事是什么呢?!绝对不是直接就开发了,而是将Git切换到这个目录内部,然后新建一个分支,在新的分支上进行改动,这一点是重中之重,可以防止我们将原来的文件给修改了之后恢复不了的问题。在我们的项目中默认只有一个主分支master,我们在任何时间不能在这个分支上进行任何的开发。
git checkout -b 分支名
这样我们进行的开发都是在这个新的分支上开发了,不会影响主分支的结果,比如我们在新的分支上放入一个文件。
如果直接切换到主分支,此时因为没有保存这个改变,我们在主分支也能看到这个文件了,这是因为我们在没有保存这些改变到新分支上面的时候,这些文件只能算是文件系统上面的文件自然能被所有分支看见,其实对分支内部的数据结构来说是透明的。
但是我们接下来在切换回新分支,然后保存之后在切换回主分支看看,这样也解决了我们的一个疑惑,如果随便扔到分支里面一个文件就要承认的话,那么使用起来也太需要小心了。
上面我们使用了:
git add .
git commit -m "描述信息"
接下来我们正式将开发好的新分支合并到主分支上面:
git checkout master git pull git checkout "新分支" git rebase -i master git checkout master git merge "新分支"
zyr@DESKTOP-RS2P5FV MINGW64 /d/GitTest (zyr_first) $ git checkout master Switched to branch 'master' Your branch is up to date with 'origin/master'. zyr@DESKTOP-RS2P5FV MINGW64 /d/GitTest (master) $ git pull Already up to date. zyr@DESKTOP-RS2P5FV MINGW64 /d/GitTest (master) $ git checkout zyr_first Switched to branch 'zyr_first' zyr@DESKTOP-RS2P5FV MINGW64 /d/GitTest (zyr_first) $ git rebase -i master Successfully rebased and updated refs/heads/zyr_first. zyr@DESKTOP-RS2P5FV MINGW64 /d/GitTest (zyr_first) $ git checkout master Switched to branch 'master' Your branch is up to date with 'origin/master'. zyr@DESKTOP-RS2P5FV MINGW64 /d/GitTest (master) $ git merge zyr_first Updating 4b1509b..313111a Fast-forward test1.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 test1.txt zyr@DESKTOP-RS2P5FV MINGW64 /d/GitTest (master) $ git push Enumerating objects: 4, done. Counting objects: 100% (4/4), done. Delta compression using up to 2 threads. Compressing objects: 100% (2/2), done. Writing objects: 100% (3/3), 331 bytes | 331.00 KiB/s, done. Total 3 (delta 0), reused 0 (delta 0) To github.com:zyrtest/GitTest.git 4b1509b..313111a master -> master zyr@DESKTOP-RS2P5FV MINGW64 /d/GitTest (master) $
我们可以看到新的改动已经成功的提交了。
我们整理一下思路,首先新建一个分支(checkout -b),然后在新分支上面开发,之后将开发的内容绑定到新分支(add,commit),然后切换回主分支,使用pull命令将最新版本的项目再次下载下来,这一点是非常重要的,如果没有这一步,其他人可能在我们下载、修改、到提交的这么长的时间段已经将新的内容提交上去了,如果我们直接将很久以前我们下载的东西和我们自己修改的东西结合到一起提交的话使用push命令,可能造成不可挽回的结果,那就是其他人在这个时间段提交的东西全部都没有了,很容易被炒鱿鱼的~~~然后我们再次切换回新分支,让新分支挂载到master的基线上面(rebase),之后我们再次切换回master,然后进行最终的合并,merge,最后我们提交,其实这个时候我们也是有一定风险的,假设在我们push的同时,其他人也在push,这个概率太小了,但是也有可能发生的,如果是这样,总有一个人的会被刷掉,这样某一方就需要重新下载最新的,合并之后然后提交了,但是开发团队人比较少,这种概率小得几乎不用考虑。
2.5、总结整个过程
下面我们总结一下整个流程,在这里我使用操作来表达:
最后一步,非常重要,不能忘记!
这就是使用Git进行项目开发的几个过程,需要注意:只有在提交的时候才能在master目录,其余的情况下都要在自己创建的分支中。
2.6、基本设置
当然还有一些基本的设置:
git config --global user.email "邮件名称"
git config --global user.name "用户名"
基本的查看命令:
git branch -a #查看所有分支 git remote -v #查看远程仓库
git status #查看分支提交状态
git checkout #查看当前分支
同样的还能使用别名:这样可以少写很多东西,非常的方便。
git config --global alias.st status git config --global alias.co checkout git config --global alias.ci commit git config --global alias.br branch git config --global alias.unstage 'reset HEAD' # 撤销暂存区的修改 git config --global alias.last 'log -1' # 显示最后一次提交 git config --global alias.lg "log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit"
.gitignore文件
有些时候,我们必须把某些文件放到Git工作目录中,但又不能提交它们,比如保存了数据库密码的配置文件等等,这个问题解决起来也很简单,在Git工作区的根目录下创建一个特殊的.gitignore文件,然后把要忽略的文件名填进去,Git就会自动忽略这些文件。
忽略某些文件时,需要编写.gitignore;网上已经有针对不同语言的ignore内容了,因此,我们只用再加上自己的一些需要忽略的文件就可以了。
.gitignore文件本身要放到版本库里,并且可以对.gitignore做版本管理!
忽略文件的原则是:
1、忽略操作系统自动生成的文件,比如缩略图等;
2、忽略编译生成的中间文件、可执行文件等,也就是如果一个文件是通过另一个文件自动生成的,那自动生成的文件就没必要放进版本库,比如Java编译产生的.class文件;
3、忽略带有敏感信息的配置文件,比如存放口令的配置文件。
忽略特定文件:.gitignore文件
git check-ignore -v 文件名
# 如果发现可能是.gitignore
写得有问题,需要找出来到底哪个规则写错了,可以用git check-ignore
命令检查
这里我们看一个例子:
我们新建一个文件叫做“.gitignore”,在文件里写上我们不想提交但是又必须使用的那个文件,比如这里我再创建一个文件:
然后我们使用git status看一下,之后我们发现发生改变的只有.gitignore,而没有了我们想要忽略的文件,这样就等于是自动过滤掉了这个文件,然后我们将发生改变的.gitignore文件上传即可。
在这里我直接在主分支上修改了,是一个不好的习惯,大家一定要新建一个分支再修改,不然可能会出现错误。
同时我们也能查看是否将我们的某些文件放到.gitignore里面了:
2.7、查看和管理操作
查看不同: git diff # 查看工作区与仓库的不同 git diff HEAD # 查看工作区与当前分支最新commit之间的差异 git diff <file> # 查看当前文件与仓库文件的不同 git diff <commit_1>...<commit_2> # 查看两次提交之间的差异 查看历史记录: git log -3 # 显示过去3次提交 git log --pretty=oneline # 只显示commit_id和提交注释 git log --graph # 显示为分支合并图 git log --abbrev-commit # 显示缩略commit_id git log --stat # 显示每次commit发生变更的文件 git log -S <keyword> # 根据关键词搜索提交历史 git log --follow <file> # 显示文件的版本历史,同 git whatchanged <file> git log -p <file> # 详细列出与文件相关的每一次diff git reflog #查看每一次命令和操作
跳转至指定版本(commit) HEAD 当前版本号,HEAD^ 上一个版本号,HEAD^^ 上上个版本号,HEAD~100 上一百个版本号 git reset --hard <commit_id>
撤销修改: git checkout [<commit_id|branch|tag>] [--] <file> # 恢复版本库的指定版本到工作区,省略commit_id则从暂存区进行恢复, #--用于避免commit_id|branch|tag与file重名 git reset HEAD <file> # 撤销暂存区的修改
删除文件: git rm <file> # 删除工作区文件,并将这次删除放入暂存区 git commit -m "delete an exist file" git rm --cached <file> # 停止追踪指定文件,保留在工作区
文件改名: git mv <oldname> <newname> # 改名文件,并将这次改名放入暂存区 git commit -m "rename an file oldname to newname"
显示指定版本(commit):
git show <commit_id> git show --name-only <commit_id> # 显示某次提交发生变化的文件 git show <commit_id>:<file> # 显示某次提交时,某个文件的内容
2.8、本地和远程的联系
1、关联本地库与远程库,origin为该远程库在本地的名称 git remote add origin git@XXX.git git remote add origin https://github.com/XXX.git 2.将本地库的内容推送到远程库 git push [-u] [--all] [--force] origin <local_b>:<remote_b> # 将本地local_b分支的更新推送到远程库origin的remote_b分支, # (-u)表示把两者关联起来,同时指定origin为默认主机, # (--all)表示推送本地所有分支, # (--force)表示强行推送,即使有冲突 git push origin <local_b> # 将本地local_b分支的更新推送到远程库origin的相同名称分支,如果后者不存在则会被创建 git push origin :<remote_b> # 推送一个空的本地分支到远程分支,等于删除指定的远程分支 git push origin # 将本地当前分支的更新推送到远程库origin中有追踪关系的远程分支 git push # 适用于当前分支只有一个追踪分支的情况,或指定默认主机的情况 git push origin <tag_name> # 推送一个本地标签到远程库 git push origin --tags # 推送全部未推送过的本地标签 git push origin :refs/tags/<tag_name> # 删除一个远程标签 3.克隆远程库到本地库,自动关联,且远程库的默认名称为origin git clone git@XXX.git git clone [-o origin] <addr> [local_dir_name] # 指定远程库名称和本地目录名称 4.查看远程库 git remote git remote -v # 显示远程库的地址,包括fetch和push地址 git remote show origin # 显示远程库的详细信息 git remote add origin git@XXX.git # 添加远程库 5.获取远程库 git fetch # 更新所有远程库所包含分支的最新commit_id,并记录到.git/FETCH_HEAD文件中 git fetch origin # 更新远程库origin的所有分支的最新commit git fetch origin <remote_b> # 更新远程库origin的remote_b分支 git fetch origin <remote_b>:<local_b> # 更新远程库origin的remote_b分支,并在本地创建local_b分支保存远程分支的数据 git pull [--rebase] origin <remote_b>:<local_b> # 获取远程库origin的remote_b分支的更新,然后与本地local_b分支合并 git pull origin <remote_b> # 获取远程库origin的remote_b分支的更新,然后与本地当前分支合并 git pull origin # 获取远程库origin中与本地当前分支有追踪关系的远程分支的更新,然后合并 git pull # 适用于当前分支只有一个追踪分支的情况 6.建立关联/追踪关系tracking git branch --set-upstream <local_b> origin/<remote_b> # 指定local_b分支追踪origin/remote_b分支
2.9、分支管理
分支: 1.创建分支 git branch <branch_name> [<start_point>] # 当指定start_point时,即为当前分支的某一次commit来创建分支 2.切换分支 git checkout <branch_name> git checkout <commit_id> # 切换到某个commit,进入detached HEAD状态 3.创建并切换分支 git checkout -b <branch_name> [<start_point>] # 等价于前两步操作 4.查看分支 git branch # 查看本地分支,当前分支前面会标一个*号 git branch -r # 查看远程分支 git branch -a # 查看所有分支 5.合并分支,合并模式:Fast-forward快进模式,优先使用,但删除分支会丢失分支信息 git merge <branch_name> # 合并指定分支到当前分支 git merge --no-ff -m "merge with a new commit" <branch_name> # 强制禁用ff模式,merge时会生成一个新的commit git rebase [-i] <branch_name> # 将当前分支的commit取消掉,并临时保存为补丁, #然后把当前分支更新为指定分支,最后把保存的补丁应用到该分支上,(-i)打开交互模式 git rebase --continue # 当有冲突并解决完成时,使用git-add命令更新,然后执行命令继续应用余下的补丁 git rebase --abort # 中断合并并恢复 6.删除分支 git branch -d <branch_name> git branch -D <branch_name> # 强行删除一个未合并的分支 git branch -dr origin/<remote_b> # 删除一个远程分支 7.隐藏当前工作区的修改,便于创建bug分支 git stash # 隐藏 ...debug... git stash list # 查看隐藏内容列表 git stash apply [stash@{i}] # 恢复指定隐藏内容 git stash drop [stash@{i}] # 删除指定隐藏内容 git stash pop [stash@{i}] # 恢复并删除指定隐藏内容
这里说一下stash,暂存工作区。我们在已有的分支中,修改一个文件的部分内容,注意是修改,不是创建新的文件。如果我们此时还没完成这个分支的工作,但是必须跳到其他分支去处理任务,我们使用git stash暂存当前的修改,可以看到工作树又干净了,以前的修改不见了,又恢复到了没修改之前的状态,然后我们跳到新的分支去继续修改其他的任务,当修改完毕之后,在新的分支上可能有保存和提交,以及最终push,然后我们回到本分支,使用git stash pop命令就可以恢复到之前的状态了,然后继续修改,最终提交即可。
2.10、标签管理
标签作用: 在开发的一些关键时期,使用标签来记录这些关键时刻, 例如发布版本, 有重大修改, 升级的时候, 会使用标签记录这些时刻, 来永久标记项目中的关键历史时刻;
标签 1.创建标签 git tag <tag_name> # 给当前分支的最新commit(即HEAD)创建标签 git tag <tag_name> <commit_id/abbrev_commit_id> # 给指定commit 创建标签 git tag -a <tag_name> -m "create a new tag on a commit" <commit_id/abbrev_commit_id> # 创建带有说明的标签 git tag -s <tag_name> -m "create a new tag on a commit" <commit_id/abbrev_commit_id> # 用PGP签名标签 2.查看标签 git tag git show <tag_name> # 查看指定标签 3.删除标签 git tag -d <tag_name>
三、总结
git有很多操作,真正使用的其实也就那几个,但是有的时候我们的项目非常复杂,需要很多其它的功能的时候,这些看似不重要的功能就有用了,在学习的时候我们实用即可,不需要掌握所有的操作,但是要能够稍微了解一下这些功能,在遇到复杂问题时能够去很好的解决,关于Git的说明就到这里,基本上能够满足我们开发的需要。
参考文献:https://blog.csdn.net/q449560173/article/details/65628261