赞
踩
没接触过:平时接触的代码还托管在SVN或CVS等工具上。
不太熟悉:可能对Git的使用还不太熟悉和全面,导致了在使用Git时步步为营。
对于频繁改动和改进的交付物,非常有必要去记录下每次变更的内容,每次记录的内容汇成了一段修改的历史,有了历史我们才知道我们曾经做了什么
记录的历史中必须要包含一些重要的信息,这样追溯才变得有意义,比如:
Who:是谁执行的变更?
When:什么时候做出的变更?
What:这次变更做了什么事情?
最好可以支持撤销变更,不让某一个提交的严重问题,去污染整个提交历史。
你可以看到我的变更提交。
我也可以看到你的变更提交。
如果双方都进行了变更提交,也可以以某种方式方法进行比对和合并,最终作出统一的变更版本。
提交对象(git commit object):每一个提交在Git中都通过git commit object存储,对象具有一个全局唯一的名称,叫做 revision hash。它的名字是由 SHA-1 算法生成,形如“998622294a6c520db718867354bf98348ae3c7e2",我们通常会取其缩写方便使用,如"9986222"。
对象构成:commit对象包含了author + commit message的基本信息。
对象存储:git commit object保存一次变更提交内的所有变更内容,而不是增量变化的数据delta(很多人都理解错了这一点),所以Git对于每次改动存储的都是全部状态的数据。
大对象存储:因对于大文件的修改和存储,同样也是存储全部状态的数据,所以可能会影响Git使用时的性能(glfs可以改进这一点)。
提交树: 多个commit对象会组成一个提交树,它让我们可以轻松的追溯commit的历史,也能对比树上commit与commit之间的变更差异。
$ git init hackers
$ cd hackers
$ git status
➜ hackers git:(master) git status
On branch master
No commits yet
nothing to commit (create/copy files anduse "git add" to track)
对于刚刚创建空仓库来说,master是我们的默认分支,一个Git仓库下可以有很多分支(branches),具体某一个分支的命名可以完全由你自己决定,通常会起便于理解的名字,如果用hash号的话肯定不是一个好主意。
branches是一种引用(ref),他们指向了一个确定的commit hash号,这样我们就可以明确我们的分支当前的内容
除了branches引用以外,还有一种引用叫做tags,相信大家也不会陌生。
master通常被我们更加熟知,因为大多数的分支开发模式都是用master来指向“最新”的commit。
On branch master代表着我们当前是在master分支下操作,所以每次当我们在提交新的commit时,Git会自动将master指向我们新的commit,当工作在其他分支上时,同理。
有一个很特殊的ref名称叫做“HEAD",它指向我们当前正在操作的branches或tags(正常工作时),其命名上非常容易理解,表示当前的引用状态。
通过 git branch(或 gittag)命令你可以灵活的操作和修改branches或tags。
对于空仓库来说,目前我们还没有进行任意的提交
工作区:工作区指的是我们本地工作的目录,比如我们可以在刚才创建的hackers目录下新增一个readme文件,readme文件这时只是本地文件系统上的修改,还未存储到Git。
暂存(索引)区:暂存实际上是将我们本地文件系统的改动转化为Git的对象存储的过程
仓库:git commit后将提交对象存储到Git仓库
通过git add命令将改动暂存
可以使用git add -p来依次暂存每一个文件改动,过程中我们可以灵活选择文件中的变更内容,从而决定哪些改动暂存
如果git add不会暂存被ignore的文件改动
通过git rm命令,我们可以删除文件的同时将其从暂存区中剔除
通过git reset命令进行修正,可以先将暂存区的内容清空,在使用git add -p命令对改动review和暂存
这个过程不会对你的文件进行任何修改操作,只是Git会认为目前没有改动需要被提交
如果我们想分阶段(or分文件)进行reset,可以使用git reset FILE or git reset -p命令
可以用git diff --staged依次检查暂存区内每一个文件的修改
用git diff查看剩余的还未暂存内容的修改
当你对需要修改的内容和范围满意时,你就可以将暂存区的内容进行commit了,命令为:git commit
如果你觉得需要把所有当前工作空间的修改全部commit,可以执行git commit -a,这相当于先执行git add后执行git commit,将暂存和提交的指令合二为一,这对于一些开发者来说是很高效的,但是如果提交过大这样做通常不合适。
我们建议一个提交中只做一件事,这在符合单一职责的同时,也可以让我们明确的知道每一个commit中做了一件什么事情而不是多个事情。所以通常我们的使用习惯都是执行git add -p来review我们将要暂存内容是否合理?是否需要更细的拆分提交?这些优秀的工程实践,将会让代码库中的commits更加优雅☕️
commit包含的信息?
commit是如何表示的?
暂存区是什么?如何全部添加、一次添加、删除、查询和修正?
如何将暂存区的改动内容commit?
不要做大提交,一个提交只做一件事
工作区(Working Directory)
暂存区(Index)
Git仓库(Git Repo)
本地改动文件时,此时还仅仅是工作区内的改动
当执行git add之后,工作区内的改动被索引在暂存区
当执行git commit之后,暂存区的内容对象将会存储在Git仓库中,并执行更新HEAD指向等后续操作,这样就完成了引用与提交、提交与改动快照的一一对应了。
查看commit历史:git log(or git log --oneline)
在commit中查看改动的diff:git log -p
查看ref与提交的关联关系,如当前master指向的commit: git show master
检出覆盖:git checkout NAME【如果NAME是一个具体的提交哈希值时,Git会认为状态时“detached(分离的)”,因为gitcheckout过程中重要的一步是将HEAD指向那个分支的最后一次commit。所以如果这样做,将意味着没有分支在引用此提交,所以若我们这时候进行提交的话,没有人会知道它们的存在】
使用git revert NAME来对commit进行反转操作
使用git diff NAME..将旧版本与当前版本进行比较,查看diff
使用git log NAME,查看指定区间的提交
使用git reset NAME进行提交重置操作
使用git reset --hard NAME:将所有文件的状态强制重置为NAME的状态,使用上需要小心
git branch b命令可以让我们创建一个名称为b的分支
当我们创建了一个b分支后,这也相当于意味着b的指向就是HEAD对应的commit
我们可以先在b分支上创建一个新的commitA ,然后假如切回master分支上,这时再提交了一个新的commitB,那么master和HEAD将会指向了新的commit __B,而b分支指向的还是原来的commit A
git checkout b可以切换到b分支上,切换后新的提交都会在b分支上,理所应当
git checkout master切换回master后,b分支的提交也不会带回master上,分支隔离
commit 14: add feature x – maybe even witha commit message about x!
commit 13: forgot to add file
commit 12: fix bug
commit 11: typo
commit 10: typo2
commit 9: actually fix
commit 8: actually actually fix
commit 7: tests pass
commit 6: fix example code
commit 5: typo
commit 4: x
commit 3: x
commit 2: x
commit 1: x
操作选项p意味着保持原样什么都不做,我们可以通过vim中编辑提交的顺序,使其在提交树上生效
操作选项r:我们可以修改提交信息,这种方式比commit --amend要好的多,因为不会新生成一个commit
操作选项e:我们可以修改commit,比如新增或者删除某些文件改动
操作选项s:我们可以将这个提交与其上一次的提交进行合并,并重新编辑提交信息
操作选项f:f代表着“fixup"。例如我们如果想针对之前一个老的提交进行fixup,又不想做一次新的提交破坏提交树的历史的逻辑含义,可以采用这种方式,这种处理方式非常优雅
每个人都有整个存储库的本地副本(其中不仅包含了自己的,也包含了其他人的提交到仓库的所有内容)。
一些VCS是集中式的(例如,SVN):服务器具有所有提交,而客户端只有他们“已检出”的文件。所以基本上在本地我们只有当前文件,每次涉及本地不存在的文件操作时,都需要访问服务端进行进一步交互。
每一个本地副本都可以当作服务端对外提供Git服务
我们可以用git push推送本地内容到任意我们有权限的Git远端仓库
不管是集团的Force、GitHub、GitLab等工具,其实本质上都是提供的Git仓库存储的相关服务,在这一点上其实并没有特别之处,针对Git本身和其协议上是透明的。
找到HEAD和NAME的一个共同祖先(common base)
尝试将这些NAME到共同祖先之间的修改合并到HEAD上
新创建一个merge commit对象,包含所有的这些变更内容
HEAD指向这个新的mergecommit
git fetch REMOTE
git merge REMOTE/BRANCH
和git push一样,有的时候需要先设置 "tracking"(-u) ,这样可以将本地和远程的分支一一对应。
冲突只是因为Git不清楚你最终要合并后的文本是什么样子,这是很正常的情况
产生冲突时,Git会中断合并操作,并指导你解决好所有的冲突文件
打开你的冲突文件,找到 <<<<<<< ,这是你需要开始处理冲突的地方,然后找到=======,等号上面的内容是HEAD到共同祖先之间的改动,等号下面是NAME到共同祖先之间的改动。用git mergetool通常是比较好的选择,当然现在大多数IDE都集成了不错的冲突解决工具
当你把冲突全部解决完毕,请用git add . 来暂存这些改动吧
最后进行git commit,如果你想放弃当前修改重新解决可以使用git merge --abort,非常方便
当你完成了以上这些艰巨的任务,最后git push吧!
会判断REMOTE的当前commit是不是你当前正在pushing commit的祖先。
如果是的话,代表你的提交是相对比较新的,push是可以成功的(fast-forwarding)
否则push失败并提示你其他人已经在你push之前执行更新(push is rejected)。
使用git pull合并远程的最新更改(git pull相当于git fetch + git merge)
使用--force强制推送本地变化到远端饮用进行覆盖,需要注意的是 这种覆盖操作可能会丢失其他人的提交内容
可以使用--force-with-lease参数,这样只有远端的ref自上次从fetch后没有改变时才会强制进行更改,否则reject the push,这样的操作更安全,是一种非常推荐使用的方式。
如果rebase操作了本地的一些提交,而这些提交之前已经push过了的话,你可能需要进行force push了,可以想象看为什么?
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。