做一次git的观众

Git这个强大的版本管理系统,工作的时候默默注视着你的代码目录,所有的操作几乎都在.git目录中完成。今天我们来做一次git的观众,以便深入了解git的各个操作。

首先,新建一个目录:git-monitor,进入目录后,用下面的命令初始化一个git仓库:

$> git init --bare git-monitor.git

然后,创建一个工作目录wp1,意思为working_copy_1,进入该目录,运行git init,以创建.git目录。

进入.git目录,会发现下列文件和目录:

HEAD config description hooks/ info/ objects/ refs/

这些都是git的演员。我们当观众的,就从监控这些文件开始。但是演员分主角、配角,和跑龙套的,在这些文件中,config是配置文件,内容不会变的;hooks中的文件是一些回调程序的例子,删掉都没关系;description文件只为某些git的web应用提供描述信息。它们都是跑龙套的,剩下的文件和目录有:

HEAD info/ objects/ refs/

在后续的操作中,还有两位要上场,分别是index文件和logs目录,至此,主要演员表为:

HEAD index info/ objects/ refs/ logs/

要用肉眼盯着它们看,实在不容易,于是我写了个ruby小程序(下载链接在最后),用于监控这些目录,一旦目录和文件有变化,就在控制台上向我们报告。我把这个程序放到.git目录下,并把它跑起来。接下来,好戏就开演了。

回到wp1目录。新建一个文件file1.txt,然后看看监控程序,发现没有任何输出,说明git对刚才的操作没有响应。既然没反应,那我就接着操作,在file1.txt中加一行内容

content added by wp1, 1st time

再看看监控,还是没有反应。看来,只要我们不调用git命令,它就不会有反应。那我就调一个看看:

$> git add .

再看看监控,终于有反应了:

Created file: index (in dir: git-monitor/wp1/.git)
Created file: c2/a04aa8cba9ba9a7a2fb8c9ecf74a3a0fc5e3fc (in dir: git-monitor/wp1/.git/objects)

git add这个命令,根据`man git-add`的解释,是把某个文件加入到index。这个index实际上就是工作目录下文件和目录状态的一个快照(stage),每一次git提交所产生的tree对象,就是依据index文件产生的(对index同志的详细采访,可以参考[url=http://progit.org/book/zh/ch9-2.html]这里[/url])。

我们来看看产生的那个object到底是什么,根据git的规则,object的目录名加文件名,和起来是一个40字符的字符串,它是对文件内容进行SHA1 digest之后,用16进制编码得到的结果。此文件的内容是二进制的,要查看它,就要用下面的命令:

$> git cat-file -t c2a04aa8cba9ba9a7a2fb8c9ecf74a3a0fc5e3fc
blob
$> git cat-file -p c2a04aa8cba9ba9a7a2fb8c9ecf74a3a0fc5e3fc
content added by wp1, 1st time

其中,-t这个参数是为了显示object类型,-p这个参数,是为了显示object的内容。显然,这个object就是刚才加进去的file1.txt,它是一个blob类型的对象,只存储文件内容和长度。

接下来,我把这次添加的内容提交一下(git commit -m 'commit by wp1, 1st time'),再看看监控,又有输出了,这次的内容还真丰富啊:

Changed file: index
Created file: 16/71ae856c149673436da08f1ba026469c3a918d (in dir: git-monitor/wp1/.git/objects)
Created file: 30/c64c3a55b02f4c251565ef057d402f84751b56 (in dir: git-monitor/wp1/.git/objects)
Created file: heads/master (in dir: git-monitor/wp1/.git/refs)
Created file: HEAD (in dir: git-monitor/wp1/.git/logs)
Created file: refs/heads/master (in dir: git-monitor/wp1/.git/logs)

首先,我们发现index文件被改变了。但是,经过我仔细比对两次的index文件的二进制字节码后发现,它的内容并没有改变,所以可能是它的修改时间发生了改变。对此我想说的是:请高人指点!

再看后面新生成的两个文件,用我们上面的办法看看内容:

$> git cat-file -p 1671ae856c149673436da08f1ba026469c3a918d
tree 30c64c3a55b02f4c251565ef057d402f84751b56
author Kevin Fu <corntrace@email.com> 1281230735 +0800
committer Kevin Fu <corntrace@email.com> 1281230735 +0800

commit by wp1, 1st time

$> git cat-file -p 30c64c3a55b02f4c251565ef057d402f84751b56
100644 blob c2a04aa8cba9ba9a7a2fb8c9ecf74a3a0fc5e3fc file1.txt

显然,第一个文件是个commit对象,第二个文件是个tree对象,从引用关系来看,是先生成的tree对象,再生成的commit对象。注意,这个commit对象没有parent引用。

再看看后面生成的refs,用git show-refs可以查看所有refs的内容

$> git show-refs
1671ae856c149673436da08f1ba026469c3a918d refs/heads/master

master当然指的是master分支,它的值指向刚才看到的commit对象

最后就是两个log文件。log文件虽然只是供人查看,但在git中的地位非同一般。先看看其内容:

$ cat logs/HEAD
0000000000000000000000000000000000000000 1671ae856c149673436da08f1ba026469c3a918d Kevin Fu <corntrace@email.com> 1281230735 +0800 commit (initial): commit by wp1, 1st time

$ cat logs/refs/heads/master
0000000000000000000000000000000000000000 1671ae856c149673436da08f1ba026469c3a918d Kevin Fu <corntrace@email.com> 1281230735 +0800 commit (initial): commit by wp1, 1st time

可以看到,这两个文件的内容目前是一样的。这个文件记录了什么呢?它记录的是工作目录的状态变化。其中,那一串0表示,一起是从零开始的,因为git在初始化工作目录时,并没有创建任何对象,要表示初始状态,只好用40个0来表示了。后面那串,就是指向刚才的本次的commit对象。这条记录解释出来,就是:在1281230735 +0800时刻,由Kevin Fu做了一次提交,工作目录从初始状态,转到commit 1671ae8对应的状态。

这个时候,用过git的人可能会生疑问:用git log看到的输出不是这个样子的呀。说对了,git log的输出,并不是由这里的log文件产生的,我认为,它的内容是根据commit之间的关联关系,实时计算并显示出来的。而这里的logs,是git中的reflog,用git reflog命令可以查看:

$> git reflog
1671ae8 HEAD@{0}: commit (initial): commit by wp1, 1st time

这个内容,就跟上面的对应上了吧。为什么说这个log文件很重要呢?因为在git中,objects其实都是死的,绝大多数情况下,一旦创建就不会被修改,也不会被删除;当版本控制的内容发生变化时,只有新的objects被创建出来,没有旧的objects被改变。那么一堆死东西,如何实现灵活的版本变化呢?第一个就是靠不断变化的版本指针,比如HEAD以及refs/heads/master文件,第二个,就是靠记录工作目录变化情况的日志文件。有了日志文件,你想查看谁就查看谁,想往哪个版本跳就往哪个版本跳,想合并谁就合并谁。许多git命令,都是基于这个思想而设计的。


接下来,我再添加一个文件file2.txt,但不是在master branch中,而是新开一个branch: advanced,我们看看开分支的时候,监控有何变化:

$> git checkout -b advanced
(monitor outputs)
Changed file: index
Changed file: HEAD
Created file: heads/advanced (in dir: /Users/corntrace/git-monitor/wp1/.git/refs)
Created file: refs/heads/advanced (in dir: /Users/corntrace/git-monitor/wp1/.git/logs)
Changed file: HEAD (in dir: /Users/corntrace/git-monitor/wp1/.git/logs)

除去那个index文件的变化,看看其他的文件。首先是HEAD文件,其内容变成了:refs/heads/advanced,说明HEAD已经移到了advanced分支上。再看看新产生的refs/heads/advanced文件:

$> git show-ref
1671ae856c149673436da08f1ba026469c3a918d refs/heads/advanced
1671ae856c149673436da08f1ba026469c3a918d refs/heads/master

可见目前它与master分支指向同一个commit。再来看看两个日志文件:

$ cat logs/HEAD
0000000000000000000000000000000000000000 1671ae856c149673436da08f1ba026469c3a918d Kevin Fu <corntrace@email.com> 1281230735 +0800 commit (initial): commit by wp1, 1st time
1671ae856c149673436da08f1ba026469c3a918d 1671ae856c149673436da08f1ba026469c3a918d Kevin Fu <corntrace@email.com> 1281236136 +0800 checkout: moving from master to advanced

它果然把我的一举一动都记录下来了。在第二条记录里,两个sha1值是一样的,说明没有提交,只有指针的创建或改变。
再看看另一个log:

0000000000000000000000000000000000000000 1671ae856c149673436da08f1ba026469c3a918d Kevin Fu <corntrace@email.com> 1281236136 +0800 branch: Created from HEAD

它说明了advanced分支是从零开始,转到commit 671ae8的。看来它一点都不含糊啊。

现在,我增加文件file2.txt,并添加以下内容,但分两次提交:第一次提交前两行,第二次提交后两行

content added by wp1, 1st time
additional content added by wp1, 1st time too

# TODO: implement a feature
# I plan to do ...

显然,第一次提交将会产生3个objects:一个commit对象,一个file2.txt的blob对象,还有一个tree对象,另外,refs/heads/advanced会指向目前的这个commit对象,然后两个log文件(logs/HEAD和logs/refs/heads/advanced)会添加一些内容,我全部列在这里:

$> git show-ref
35ba29ec8f31d5372b75d7be6b1ec7f03c0fb666 refs/heads/advanced
1671ae856c149673436da08f1ba026469c3a918d refs/heads/master

$> git cat-file -p 35ba29ec8f31d5372b75d7be6b1ec7f03c0fb666
tree 9c1c4549a869ede4d5f85c93594c1c23c311122f
parent 1671ae856c149673436da08f1ba026469c3a918d
author Kevin Fu <corntrace@email.com> 1281251100 +0800
committer Kevin Fu <corntrace@email.com> 1281251100 +0800

commit by wp1 for file2.txt, 1st time

$> git cat-file -p 9c1c4549a869ede4d5f85c93594c1c23c311122f
100644 blob c2a04aa8cba9ba9a7a2fb8c9ecf74a3a0fc5e3fc file1.txt
100644 blob baa4a1630ce88a9198b5eda885884aadab795806 file2.txt

$> git cat-file -p baa4a1630ce88a9198b5eda885884aadab795806
content added by wp1, 1st time
additional content added by wp1, 1st time

$> cat logs/HEAD
0000000000000000000000000000000000000000 1671ae856c149673436da08f1ba026469c3a918d Kevin Fu <corntrace@email.com> 1281230735 +0800 commit (initial): commit by wp1, 1st time
1671ae856c149673436da08f1ba026469c3a918d 1671ae856c149673436da08f1ba026469c3a918d Kevin Fu <corntrace@email.com> 1281236136 +0800 checkout: moving from master to advanced
1671ae856c149673436da08f1ba026469c3a918d 35ba29ec8f31d5372b75d7be6b1ec7f03c0fb666 Kevin Fu <corntrace@email.com> 1281251100 +0800 commit: commit by wp1 for file2.txt, 1st time

$> cat logs/refs/heads/advanced
0000000000000000000000000000000000000000 1671ae856c149673436da08f1ba026469c3a918d Kevin Fu <corntrace@email.com> 1281236136 +0800 branch: Created from HEAD
1671ae856c149673436da08f1ba026469c3a918d 35ba29ec8f31d5372b75d7be6b1ec7f03c0fb666 Kevin Fu <corntrace@email.com> 1281251100 +0800 commit: commit by wp1 for file2.txt, 1st time

可以看到,这次的commit对象中多了parent的引用,就是指向上一次的commit。
接下来我提交第二部分的内容,其结果与刚才的分析相似,就不写了。提交之后,我转回master分支,将advanced分支中的内容合并进来,然后将master分支推送出去,我就可以下班了。

我们看看转回master分支时,监控都有哪些输出:

Changed file: index
Changed file: HEAD
Changed file: HEAD (in dir: /Users/corntrace/git-monitor/wp1/.git/logs)

可见,除了那个index文件,HEAD文件的内容被该为:ref: refs/heads/master,logs/HEAD文件中添加了一行记录分支跳转的日志。

接下来我运行git merge advanced。合并完成后,看看监控的输出:

Changed file: index
Changed file: heads/master (in dir: /Users/corntrace/git-monitor/wp1/.git/refs)
Changed file: HEAD (in dir: /Users/corntrace/git-monitor/wp1/.git/logs)
Changed file: refs/heads/master (in dir: /Users/corntrace/git-monitor/wp1/.git/logs)

这里列出后三项的内容:

$> git show-ref
8227ae64f1f651669c6445d4c37909c8443df209 refs/heads/advanced
8227ae64f1f651669c6445d4c37909c8443df209 refs/heads/master

$> cat logs/HEAD
(猜都能猜出来,就省了吧)

$> cat logs/refs/heads/master
0000000000000000000000000000000000000000 1671ae856c149673436da08f1ba026469c3a918d Kevin Fu <corntrace@email.com> 1281230735 +0800 commit (initial): commit by wp1, 1st time
1671ae856c149673436da08f1ba026469c3a918d 8227ae64f1f651669c6445d4c37909c8443df209 Kevin Fu <corntrace@email.com> 1281252267 +0800 merge advanced: Fast-forward

从分支的指向可以看到,它指到advanced分支对应的commit上去了;从logs的内容可以看到,本次的操作称为merge advanced: Fast-forword。除此之外,并没有产生任何object,连commit都没有,这是因为在合并之后,git分析出当前的目录树结构与advanced分支中的目录树是一样的,所以只是简单的把master的指针指向advanced分支。

OK,在下班之前,我还是要看看master分支中的代码,确保没有什么坏代码被交上去。但是,我的神,file2.txt中有一个TODO,这要是被老板看见了,还不要我晚上加班啊!我能把它删掉再提交吗?不能啊,老板要是往回看一个版本,不就找出来了!因此,当务之急就是,把master的版本指针退回去,让它指向原来的1671ae8这个版本。git reset这个命令可以帮到我。

$> git reset --hard 1671ae8
(monitor outputs)
Changed file: index
Changed file: heads/master (in dir: /Users/corntrace/git-monitor/wp1/.git/refs)
Changed file: HEAD (in dir: /Users/corntrace/git-monitor/wp1/.git/logs)
Changed file: refs/heads/master (in dir: /Users/corntrace/git-monitor/wp1/.git/logs)

$> git show-ref
8227ae64f1f651669c6445d4c37909c8443df209 refs/heads/advanced
1671ae856c149673436da08f1ba026469c3a918d refs/heads/master

$> cat logs/refs/heads/master
0000000000000000000000000000000000000000 1671ae856c149673436da08f1ba026469c3a918d Kevin Fu <corntrace@email.com> 1281230735 +0800 commit (initial): commit by wp1, 1st time
1671ae856c149673436da08f1ba026469c3a918d 8227ae64f1f651669c6445d4c37909c8443df209 Kevin Fu <corntrace@email.com> 1281252267 +0800 merge advanced: Fast-forward
8227ae64f1f651669c6445d4c37909c8443df209 1671ae856c149673436da08f1ba026469c3a918d Kevin Fu <corntrace@email.com> 1281253859 +0800 1671ae8: updating HEAD

可以看到,master的head是真的退回去了,reflog中的记录,稍候再说。现在只要把advanced中想要的那个提交弄过来,就万事大吉了。git cherry-pick这个命令这是用来做这个的。

$> git cherry-pick --ff 35ba29e
(此时这里居然没有任何输出,git的作者们太高估用户了!来看看monitor outputs)
Changed file: index
Changed file: heads/master (in dir: /Users/corntrace/git-monitor/wp1/.git/refs)
Changed file: HEAD (in dir: /Users/corntrace/git-monitor/wp1/.git/logs)
Changed file: refs/heads/master (in dir: /Users/corntrace/git-monitor/wp1/.git/logs)

$> git show-ref
8227ae64f1f651669c6445d4c37909c8443df209 refs/heads/advanced
35ba29ec8f31d5372b75d7be6b1ec7f03c0fb666 refs/heads/master

$> cat file2.txt
content added by wp1, 1st time
additional content added by wp1, 1st time

看,TODO果然是没有了!在这里我用的--ff参数,是要告诉git,只需要做fast-forword就可以了,因为35ba29e这个commit的parent,正是先前的master的head(1671ae8)。

OK,我可以把master分支推送到远程仓库里去了。在推之前,我需要把远程仓库加进来。这里我就用一个本地的仓库来代替远程的吧。

$> git remote add origin /path/to/git-monitor.git
(这一步monitor无输出)

$> git push origin master
(monitor outputs)
Created file: remotes/origin/master (in dir: /Users/corntrace/git-monitor/wp1/.git/refs)
Created file: refs/remotes/origin/master (in dir: /Users/corntrace/git-monitor/wp1/.git/logs)

$> git show-ref
8227ae64f1f651669c6445d4c37909c8443df209 refs/heads/advanced
35ba29ec8f31d5372b75d7be6b1ec7f03c0fb666 refs/heads/master
35ba29ec8f31d5372b75d7be6b1ec7f03c0fb666 refs/remotes/origin/master

$> cat logs/refs/remotes/origin/master
0000000000000000000000000000000000000000 35ba29ec8f31d5372b75d7be6b1ec7f03c0fb666 Kevin Fu <corntrace@email.com> 1281255166 +0800 update by push

可见,这一步的操作就是新建了一个remote master分支的ref,然后添加了一个logs文件。看来所有的事情都办妥了,那就下班吧。

但是老板还没有下班,而是正准备做code review。他把代码checkout下来:

$> git co /path/to/git-monitor.git && cd git-monitor
$> git reflog
35ba29e HEAD@{0}: clone: from /Users/corntrace/git-monitor/git-monitor.git

他的reflog文件中,完全没有我刚才操作的内容。看来,git是不会提交reflog到仓库中的。

OK,写到这里,今天的git剧就要谢幕了。相关文件在后面下载,大家可以重播。如果你有使用git的好的case,也可以分享给大家,Thank you!
内容概要:本文提出了一种基于非合作博弈理论的居民负荷分层调度模型,并结合双层鲸鱼优化算法(Two-level Whale Optimization Algorithm)进行高效求解,模型与算法均通过Matlab代码实现。研究针对电力系统中居民侧用电负荷的复杂调度问题,引入非合作博弈机制刻画各用户之间的利益竞争关系,实现负荷的分层优化分配;同时设计双层优化架构,上层优化资源配置,下层模拟用户自主决策行为,提升了模型的实用性与合理性。通过智能优化算法求解多层级、非凸非线性的博弈模型,有效提高了调度方案的收敛性与全局寻优能力,适用于现代智能电网中的需求侧管理与能源优化场景。; 适合人群:具备电力系统基础理论知识和Matlab编程能力,从事智能电网、能源优化调度、需求侧管理、博弈论应用等方向的科研人员、高校研究生及工程技术人员。; 使用场景及目标:①应用于居民区电力负荷的分层优化调度系统设计与仿真分析;②为非合作博弈在多主体能源系统建模中的应用提供方法论支持;③利用双层鲸鱼算法解决具有嵌套结构的复杂双层优化问题,提升求解效率与调度方案的可行性。; 阅读建议:建议读者结合提供的Matlab代码深入理解模型构建逻辑与算法实现流程,重点关注博弈模型的效用函数设计、纳什均衡求解思路以及双层优化结构的迭代机制,宜配合实际用电数据开展复现实验以验证模型有效性与鲁棒性。
内容概要:本文围绕基于自适应神经模糊推理系统(ANFIS)智能控制器的可再生能源微电网功率管理系统展开研究,结合Simulink仿真实现,深入探讨了微电网中功率的智能调控与经济机组组合调度问题。通过引入ANFIS控制器,有效应对风能、光伏等可再生能源出力的波动性与不确定性,提升系统运行的稳定性与电能质量。研究内容涵盖微电网多源协调控制策略、功率平衡管理、优化调度模型构建及仿真验证,实现了对分布式电源、储能系统和负荷的协同优化,兼顾经济性与可靠性目标,并通过仿真平台验证了所提方法的有效性与优越性。; 适合人群:具备电力系统、自动化或新能源相关专业背景,熟悉Matlab/Simulink仿真环境,从事微电网能量管理、智能控制、能源优化等领域研究的研究生、科研人员及工程技术人员。; 使用场景及目标:①用于高比例可再生能源接入场景下的微电网能量管理系统研发与教学实践;②为实现微电网功率稳定控制与经济高效运行提供先进的智能控制解决方案;③支撑高水平学术论文复现、科研课题攻关及实际工程项目的仿真验证与方案优化。; 阅读建议:建议结合提供的Simulink模型与相关代码进行动手实践,重点关注ANFIS控制器的设计流程、规则库构建与参数调优方法,并通过与传统PID或MPC控制策略的对比实验,深入理解其在动态响应与鲁棒性方面的优势。同时可进一步拓展文中提出的优化调度逻辑,应用于多目标、多约束的复杂实际应用场景中。
内容概要:本文档聚焦于“直流电机双闭环控制Matlab仿真”,系统阐述了基于Matlab/Simulink平台实现直流电机双闭环控制系统(主要包括速度环与电流环)的设计与仿真全过程。通过构建直流电机的数学模型,结合PI控制器进行调控,实现对电机转速和电枢电流的高精度动态控制,验证控制策略的稳定性与响应性能。文档详细介绍了仿真模型的搭建流程、关键参数的整定方法、系统动态波形的分析手段以及仿真结果的有效性验证,体现了经典自动控制理论在实际电机系统中的工程应用,是电机控制与电力电子技术相结合的典型研究案例。; 适合人群:具备自动控制原理、电机与拖动基础、电力电子技术和Matlab/Simulink仿真能力的电气工程、自动化、机电一体化等专业的本科生、研究生及从事电机驱动系统研发的工程技术人员。; 使用场景及目标:①作为高校课程设计或实验教学材料,帮助学生深入理解双闭环调速系统的工作机理与工程实现;②服务于科研项目,为新型电机控制算法(如滑模、模糊PID等)的开发与性能对比提供基础仿真验证平台;③作为工业界产品前期设计的仿真工具,用于评估不同控制策略在动态响应、抗干扰能力和稳态精度方面的可行性。; 阅读建议:建议读者在学习过程中紧密结合自动控制理论知识,亲手在Simulink环境中搭建完整的双闭环仿真模型,通过反复调整PI控制器的比例与积分参数,观察并分析转速、电流的阶跃响应曲线,从而深刻理解反馈控制的本质、系统稳定性条件以及参数整定对动态性能的影响,进而掌握电机控制系统的设计精髓。
内容概要:本文研究了基于Benders分解与输电网运营商(TSO)和配电网运营商(DSO)协调机制的不确定环境下输配电网双层优化模型,旨在提升高比例可再生能源接入背景下电网系统的协调性与鲁棒性。模型上层以系统整体经济性为目标进行优化调度,下层采用Benders分解实现TSO与DSO之间的信息交互与协同决策,通过引入割平面迭代机制保障求解的收敛性与全局最优性。研究充分考虑新能源出力与负荷需求的不确定性,构建了具有强适应性的双层优化框架,并基于Matlab完成了模型的编程实现与仿真验证,有效解决了多主体、多层级、多不确定性因素耦合下的电力系统优化调度难题。; 适合人群:具备电力系统分析、运筹学与优化理论基础,熟悉Matlab编程环境,从事智能电网、能源互联网、分布式能源集成、电力市场等方向的研究生、科研人员及工程技术人员。; 使用场景及目标:①研究高渗透率可再生能源条件下输配电网协同优化调度策略;②掌握Benders分解在电力系统双层优化建模中的应用方法与实现技巧;③构建TSO-DSO多主体协调机制,实现跨层级电网资源的高效互动与决策解耦;④提升对不确定性建模、分解算法设计及大规模优化问题求解能力。; 阅读建议:建议读者结合Matlab代码逐模块剖析模型构建流程,重点理解Benders割的生成逻辑、主从问题的信息传递机制及收敛判据设定,推荐在标准IEEE测试系统上复现实验以深入掌握模型特性与算法性能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值