<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Fwolf's Blog &#187; git - Fwolf's Blog</title>
	<atom:link href="http://www.fwolf.com/blog/post/tag/git/feed" rel="self" type="application/rss+xml" />
	<link>http://www.fwolf.com/blog</link>
	<description>随心·随意·随缘·努力～</description>
	<lastBuildDate>Sun, 29 Aug 2010 14:52:23 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<item>
		<title>WordPress 烦人的 revision 和 auto-draft</title>
		<link>http://www.fwolf.com/blog/post/452</link>
		<comments>http://www.fwolf.com/blog/post/452#comments</comments>
		<pubDate>Mon, 05 Jul 2010 17:19:34 +0000</pubDate>
		<dc:creator>Fwolf</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[draft]]></category>
		<category><![CDATA[git]]></category>
		<category><![CDATA[revision]]></category>
		<category><![CDATA[WordPress]]></category>

		<guid isPermaLink="false">http://www.fwolf.com/blog/?p=452</guid>
		<description><![CDATA[revision 是早就有了，auto-draft 是最近才发现的，个人非常不喜欢这2个功能，偏偏 WordPress 还没有在后台中增加显式的关闭功能，所以更显得烦人。 revision 是你每保存一次 post 的时候，都把修改前的内容存成一个 revision，这样你就不用担心以前的版本找不到了。问题是，写 blog 又不是写代码，用得着这把牛刀么？就是写代码，也有不想保存的版本，基本上扔到 scm 里面就不会再看了呀。 禁用 revision 的方法，对目前的 WordPress 3.0 有效，在 wp-config.php 中添加： define&#40;'WP_POST_REVISIONS', false&#41;; define&#40;'AUTOSAVE_INTERVAL', 60000&#41;; 同时也禁止了自动保存，多手工保存吧，或者本地写好了再 post 。 auto-draft 是这样出现的，当你 new 一个 post 的时候，以前是第一次保存的时候生成 id，现在则是打开 new 页面的时候就生成了，体现在数据库中 wp_posts.post_status 为 auto-draft。这种没有内容先保存的方法一般是用来避免多人同时保存时的写入数据冲突，可一般的 blog 会频繁产生这种情况么？更糟糕的是，auto-draft 类型的post 无法在 Posts 管理中进行编辑，也就是说如果你打开了 new post 页面，输入了一些内容，然后没有保存或者发布就离开了这个页面，那么数据库中就多了一条 auto-draft “僵尸记录”，你再也找不到它了。 auto-draft 目前好像没有方法关闭，但可以从数据库中把他们更改为 draft，以后当草稿修改成新文章就是了： [...]]]></description>
			<content:encoded><![CDATA[<p>revision 是早就有了，auto-draft 是最近才发现的，个人非常不喜欢这2个功能，偏偏 WordPress 还没有在后台中增加显式的关闭功能，所以更显得烦人。</p>

<p>revision 是你每保存一次 post 的时候，都把修改前的内容存成一个 revision，这样你就不用担心以前的版本找不到了。问题是，写 blog 又不是写代码，用得着这把牛刀么？就是写代码，也有不想保存的版本，基本上扔到 scm 里面就不会再看了呀。</p>

<p>禁用 revision 的方法，对目前的 WordPress 3.0 有效，在 <code>wp-config.php</code> 中添加：</p>


<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #990000;">define</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'WP_POST_REVISIONS'</span><span style="color: #339933;">,</span> <span style="color: #009900; font-weight: bold;">false</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #990000;">define</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'AUTOSAVE_INTERVAL'</span><span style="color: #339933;">,</span> <span style="color: #cc66cc;">60000</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>


<p>同时也禁止了自动保存，多手工保存吧，或者本地写好了再 post 。</p>

<p>auto-draft 是这样出现的，当你 new 一个 post 的时候，以前是第一次保存的时候生成 id，现在则是打开 new 页面的时候就生成了，体现在数据库中 <code>wp_posts.post_status</code> 为 <code>auto-draft</code>。这种没有内容先保存的方法一般是用来避免多人同时保存时的写入数据冲突，可一般的 blog 会频繁产生这种情况么？更糟糕的是，auto-draft 类型的post 无法在 Posts 管理中进行编辑，也就是说如果你打开了 new post 页面，输入了一些内容，然后没有保存或者发布就离开了这个页面，那么数据库中就多了一条 auto-draft “僵尸记录”，你再也找不到它了。</p>

<p>auto-draft 目前好像没有方法关闭，但可以从数据库中把他们更改为 draft，以后当草稿修改成新文章就是了：</p>


<div class="wp_syntax"><div class="code"><pre class="sql" style="font-family:monospace;">mysql<span style="color: #66cc66;">&gt;</span> <span style="color: #993333; font-weight: bold;">SELECT</span> <span style="color: #993333; font-weight: bold;">DISTINCT</span> post_status<span style="color: #66cc66;">,</span> count<span style="color: #66cc66;">&#40;</span><span style="color: #cc66cc;">1</span><span style="color: #66cc66;">&#41;</span> <span style="color: #993333; font-weight: bold;">FROM</span> wp_posts <span style="color: #993333; font-weight: bold;">GROUP</span> <span style="color: #993333; font-weight: bold;">BY</span> post_status;
mysql<span style="color: #66cc66;">&gt;</span> <span style="color: #993333; font-weight: bold;">UPDATE</span> wp_posts <span style="color: #993333; font-weight: bold;">SET</span> post_status<span style="color: #66cc66;">=</span><span style="color: #ff0000;">'draft'</span> <span style="color: #993333; font-weight: bold;">WHERE</span> post_status<span style="color: #66cc66;">=</span><span style="color: #ff0000;">'auto-draft'</span>;</pre></div></div>


<p>最后，贡献一个 php 脚本，自动把 revision 和 auto-draft 都修改成草稿 draft，并且找出数据库中不连续的 post id，把他们也都存成草稿，这样可以保持 url 中 id 的连续性，似乎更加美观和整洁。未经严格测试，请参考使用：</p>

<script src="http://gist.github.com/464533.js?file=gistfile1.php"></script>

	Tags: <a href="http://www.fwolf.com/blog/post/tag/draft" title="draft" rel="tag">draft</a>, <a href="http://www.fwolf.com/blog/post/tag/git" title="git" rel="tag">git</a>, <a href="http://www.fwolf.com/blog/post/tag/revision" title="revision" rel="tag">revision</a>, <a href="http://www.fwolf.com/blog/post/tag/wordpress" title="WordPress" rel="tag">WordPress</a><br />

	<h4>Related posts</h4>
	<ul class="st-related-posts">
	<li><a href="http://www.fwolf.com/blog/post/102" title="更改 WordPress 自动生成摘要的方式 (2005-12-19)">更改 WordPress 自动生成摘要的方式</a> (19)</li>
	<li><a href="http://www.fwolf.com/blog/post/87" title="搞定WordPress的Permalinks (2005-11-07)">搞定WordPress的Permalinks</a> (6)</li>
	<li><a href="http://www.fwolf.com/blog/post/241" title="如何为网站添加sitemap文件 (2006-10-17)">如何为网站添加sitemap文件</a> (3)</li>
	<li><a href="http://www.fwolf.com/blog/post/402" title="升级WP旧模板，让它支持Widgets (2008-05-11)">升级WP旧模板，让它支持Widgets</a> (4)</li>
	<li><a href="http://www.fwolf.com/blog/post/129" title="升级WordPress从1.5到2.0 (2006-01-19)">升级WordPress从1.5到2.0</a> (5)</li>
</ul>

]]></content:encoded>
			<wfw:commentRss>http://www.fwolf.com/blog/post/452/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Git 合并 patch 时的冲突处理一例</title>
		<link>http://www.fwolf.com/blog/post/448</link>
		<comments>http://www.fwolf.com/blog/post/448#comments</comments>
		<pubDate>Tue, 25 Aug 2009 15:02:35 +0000</pubDate>
		<dc:creator>Fwolf</dc:creator>
				<category><![CDATA[Svn/Git]]></category>
		<category><![CDATA[conflict]]></category>
		<category><![CDATA[example]]></category>
		<category><![CDATA[git]]></category>
		<category><![CDATA[import]]></category>
		<category><![CDATA[merge]]></category>
		<category><![CDATA[patch]]></category>

		<guid isPermaLink="false">http://www.fwolf.com/blog/?p=448</guid>
		<description><![CDATA[git version 1.6.0.4 几个新手刚刚开始接触 Git，为了维护核心仓库的“纯洁”，避免太多无关信息被误提交进仓库（再次批评一些图形化工具默认的“Select All”），采用了核心仓库只读，邮件提交 patch，审核后再提交的工作流程。 期间有时会遇到合并冲突，正常的原因一般是未及时下载新版本产生了冲突，特殊一点的原因是手工修改 patch 内容导致的。有时候看注释写得不够准确，忍不住就改了，有时候是 Geany 保存时自动去除了 patch 原文中的行尾空格，有时候是文件回车格式、BOM 等变动了，总之合并 patch 的时候，如果生成 patch 的“原稿”找不到，一般就产生了冲突，比如： $ git am 0001-BUG-Sybase.patch Applying: CHG: 读取Sybase如果时间为空,设置默认时间的修改 error: patch failed: source.php:38 error: source.php: patch does not apply Patch failed at 0001. When you have resolved this problem run "git am --resolved". If you would prefer to [...]]]></description>
			<content:encoded><![CDATA[<p>git version 1.6.0.4</p>

<p>几个新手刚刚开始接触 Git，为了维护核心仓库的“纯洁”，避免太多无关信息被误提交进仓库（再次批评一些图形化工具默认的“Select All”），采用了核心仓库只读，邮件提交 patch，审核后再提交的工作流程。</p>

<p>期间有时会遇到合并冲突，正常的原因一般是未及时下载新版本产生了冲突，特殊一点的原因是手工修改 patch 内容导致的。有时候看注释写得不够准确，忍不住就改了，有时候是 Geany 保存时自动去除了 patch 原文中的行尾空格，有时候是文件回车格式、BOM 等变动了，总之合并 patch 的时候，如果生成 patch 的“原稿”找不到，一般就产生了冲突，比如：</p>

<pre><code>$ git am 0001-BUG-Sybase.patch
Applying: CHG: 读取Sybase如果时间为空,设置默认时间的修改
error: patch failed: source.php:38
error: source.php: patch does not apply
Patch failed at 0001.
When you have resolved this problem run "git am --resolved".
If you would prefer to skip this patch, instead run "git am --skip".
To restore the original branch and stop patching run "git am --abort".
</code></pre>

<p>刚开始一看有些懵，因为没有任何冲突在哪里的提示，后来找到一种方法，am 操作出问题后先手工 apply：</p>

<pre><code>$ git apply --reject 0001-BUG-Sybase.patch
Checking patch source.php...
error: while searching for:
        // 注释
        // 以下为几行代码片断
error: patch failed: source.php:38
Applying patch source.php with 1 rejects...
Rejected hunk #1.
</code></pre>

<p>这样，就把没有冲突的文件先合并了，剩下有冲突的作了标记。先看输出，error: while searching for: 说明是这段代码有冲突，error: patch failed: source.php:38 指明了产生冲突的代码<strong>片断</strong>的开始行号，相应的，patch 中应该有这么一段：</p>

<pre><code>diff --git a/source.php b/source.php
index 8770441..4e77b8a 100644
--- a/source.php
+++ b/source.php
@@ -38,27 +38,23 @@ class Site extends Module
        // 注释
        // 以下为几行代码片断
</code></pre>

<p>同时，还会产生一个 source.php.rej 文件，里面也是上面这段因为冲突无法合并的代码片断。</p>

<p>现在，在这段代码中查找冲突原因，并对文件进行修改，source.php.rej 参考完了可以删掉。改好之后，用 git add 把 source.php 添加到缓冲区，同时也要把其他没有冲突合并成功了的文件也加进来，因为在作 apply 操作的时候他们也发生了变化：</p>

<pre><code>$ git add source.php
$ git add 其他 apply 进来的文件们
</code></pre>

<p>最后：</p>

<pre><code>$ git am --resolved
Applying: CHG: 读取Sybase如果时间为空,设置默认时间的修改
</code></pre>

<p>大功告成。</p>

<p>中间如果处理乱了，用 git reset 恢复即可，所以合并 patch 在一个“干净”的分支上处理更好。</p>

	Tags: <a href="http://www.fwolf.com/blog/post/tag/conflict" title="conflict" rel="tag">conflict</a>, <a href="http://www.fwolf.com/blog/post/tag/example" title="example" rel="tag">example</a>, <a href="http://www.fwolf.com/blog/post/tag/git" title="git" rel="tag">git</a>, <a href="http://www.fwolf.com/blog/post/tag/import" title="import" rel="tag">import</a>, <a href="http://www.fwolf.com/blog/post/tag/merge" title="merge" rel="tag">merge</a>, <a href="http://www.fwolf.com/blog/post/tag/patch" title="patch" rel="tag">patch</a>, <a href="http://www.fwolf.com/blog/post/tag/scm" title="Svn/Git" rel="tag">Svn/Git</a><br />

	<h4>Related posts</h4>
	<ul class="st-related-posts">
	<li><a href="http://www.fwolf.com/blog/post/446" title="[Git]初学者注意事项 (2009-08-04)">[Git]初学者注意事项</a> (0)</li>
	<li><a href="http://www.fwolf.com/blog/post/127" title="利用SVN更新网站 (2006-01-19)">利用SVN更新网站</a> (7)</li>
	<li><a href="http://www.fwolf.com/blog/post/162" title="[ubuntu]安装vmware时找不到c header files的小问题 (2006-05-09)">[ubuntu]安装vmware时找不到c header files的小问题</a> (4)</li>
	<li><a href="http://www.fwolf.com/blog/post/441" title="[Git]真正回滚已上传的更新 (2009-05-14)">[Git]真正回滚已上传的更新</a> (0)</li>
	<li><a href="http://www.fwolf.com/blog/post/431" title="[Git]提交后自动发email (2009-03-27)">[Git]提交后自动发email</a> (2)</li>
</ul>

]]></content:encoded>
			<wfw:commentRss>http://www.fwolf.com/blog/post/448/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>[Git]初学者注意事项</title>
		<link>http://www.fwolf.com/blog/post/446</link>
		<comments>http://www.fwolf.com/blog/post/446#comments</comments>
		<pubDate>Tue, 04 Aug 2009 13:57:05 +0000</pubDate>
		<dc:creator>Fwolf</dc:creator>
				<category><![CDATA[Svn/Git]]></category>
		<category><![CDATA[git]]></category>
		<category><![CDATA[newbie]]></category>
		<category><![CDATA[rule]]></category>
		<category><![CDATA[tips]]></category>

		<guid isPermaLink="false">http://www.fwolf.com/blog/?p=446</guid>
		<description><![CDATA[实在是受不了有些人的 Git 提交，费大力气“回滚”，遂整理了这些刚开始用 git 或者还没有建立 scm 概念时容易犯的错误。 和源码无关的东西，尽量不要进仓库 不得不说一些图形化软件，在提交内容的时候大多提供一个“全选”或者“Select All”功能，这是最不好的了，一些懒惰的同志看都不看就连瓢带碗都提交了。 测试时上传的文件，测试时的临时文件，统统不要 对应上一条，强烈建议把所有文件的上传保存目录另行设置，放到源代码目录以外 编辑器产生的备份文件、临时文件，编译时的中间文件，统统不要 对应上一条，有个例外就是为了实现通过 Git 更新系统，.NET 的 bin 文件要进仓库，导致那个仓库现在都 100+m 了 图片等资源文件，进仓库也可以，但应当使用有意义的文件名，便于后期管理 对应上一条，现在设计网站界面喜欢先作图然后切割，产生一大堆 001_r5_c1.jpg 这样的文件，讨厌之极 使用的外部类库，比如 php 类、js 类等，统统扔到源码目录以外，如果实在没办法要放在目录树中，也可以留出空目录，打包发行的时候再包含进来，依然不进代码仓库 不要中文文件名，主要是跨平台使用有问题，文件名完全能够只用字母数字减号下划线 尽量采用相对小、相对独立的提交 Git 是作什么用的？Git 不是代码上传工具，也不是网站更新工具，而是软件开发过程的记录工具，为了更加准确的定位每个问题、每个功能修改，就需要在每完成一部分可以称得上是“一项”的工作时，就 commit 一次。哪怕只是修改了一两行，只要产生了必要的功能改变，就有价值记录。 当采用代码审核机制或者需要用邮件提交补丁时，较小的提交能够更有效、更容易、更准确的被检查和审核，这个在 linux kernel 开发文档中也有提到。 当然不能矫枉过正，必须有可记录的改变才有提交的价值。对应的，Git 日志大多数情况下主要显示第一行，控制每次提交都能用一句话简单概括，也是有必要的。 注释格式 格式属于个人习惯和团队规范范围，有必要采用相对统一的风格。 Git 本身不允许空注释，同时建议注释的第一行写简要说明，下面留一行空行，再写详细说明。 我的个人习惯，喜欢在每条注释前面用大约三个字母来表示本次修改的性质： Add something Bug [fix&#124;found]: describe the bug or [...]]]></description>
			<content:encoded><![CDATA[<p>实在是受不了有些人的 Git 提交，费大力气“<a href="441">回滚</a>”，遂整理了这些刚开始用 git 或者还没有建立 scm 概念时容易犯的错误。</p>

<h3>和源码无关的东西，尽量不要进仓库</h3>

<p>不得不说一些图形化软件，在提交内容的时候大多提供一个“全选”或者“Select All”功能，这是最不好的了，一些懒惰的同志看都不看就连瓢带碗都提交了。</p>

<ul>
<li>测试时上传的文件，测试时的临时文件，统统不要</li>
<li>对应上一条，强烈建议把所有文件的上传保存目录另行设置，放到源代码目录以外</li>
<li>编辑器产生的备份文件、临时文件，编译时的中间文件，统统不要</li>
<li>对应上一条，有个例外就是为了实现通过 Git 更新系统，.NET 的 bin 文件要进仓库，导致那个仓库现在都 100+m 了</li>
<li>图片等资源文件，进仓库也可以，但应当使用有意义的文件名，便于后期管理</li>
<li>对应上一条，现在设计网站界面喜欢先作图然后切割，产生一大堆 001_r5_c1.jpg 这样的文件，讨厌之极</li>
<li>使用的外部类库，比如 php 类、js 类等，统统扔到源码目录以外，如果实在没办法要放在目录树中，也可以留出空目录，打包发行的时候再包含进来，依然不进代码仓库</li>
<li>不要中文文件名，主要是跨平台使用有问题，文件名完全能够只用字母数字减号下划线</li>
</ul>

<h3>尽量采用相对小、相对独立的提交</h3>

<p>Git 是作什么用的？Git 不是代码上传工具，也不是网站更新工具，而是软件开发过程的记录工具，为了更加准确的定位每个问题、每个功能修改，就需要在每完成一部分可以称得上是“一项”的工作时，就 commit 一次。哪怕只是修改了一两行，只要产生了必要的功能改变，就有价值记录。</p>

<p>当采用代码审核机制或者需要用邮件提交补丁时，较小的提交能够更有效、更容易、更准确的被检查和审核，这个在 <a href="http://wiki.zh-kernel.org/doc/%E5%A6%82%E4%BD%95%E5%8F%82%E4%B8%8E_linux_%E5%86%85%E6%A0%B8%E5%BC%80%E5%8F%91">linux kernel 开发文档</a>中也有提到。</p>

<p>当然不能矫枉过正，必须有可记录的改变才有提交的价值。对应的，Git 日志大多数情况下主要显示第一行，控制每次提交都能用一句话简单概括，也是有必要的。</p>

<h3>注释格式</h3>

<p>格式属于个人习惯和团队规范范围，有必要采用相对统一的风格。</p>

<p>Git 本身不允许空注释，同时建议注释的第一行写简要说明，下面留一行空行，再写详细说明。</p>

<p>我的个人习惯，喜欢在每条注释前面用大约三个字母来表示本次修改的性质：</p>

<ul>
<li>Add something</li>
<li>Bug [fix|found]: describe the bug or fix.</li>
<li>Chg something</li>
<li>Del something</li>
<li>Enh some treatment</li>
<li>New something</li>
<li>Tmp for some cause</li>
</ul>

<p>为了保持语法通顺，也可以采用前三个字母后面加冒号，后面有啥写啥的方法。</p>

<p>最后，我觉得，能够遵守行业规范和团队约定，主动养成良好习惯，应当是鉴别人才的一项重要因素。</p>

	Tags: <a href="http://www.fwolf.com/blog/post/tag/git" title="git" rel="tag">git</a>, <a href="http://www.fwolf.com/blog/post/tag/newbie" title="newbie" rel="tag">newbie</a>, <a href="http://www.fwolf.com/blog/post/tag/rule" title="rule" rel="tag">rule</a>, <a href="http://www.fwolf.com/blog/post/tag/scm" title="Svn/Git" rel="tag">Svn/Git</a>, <a href="http://www.fwolf.com/blog/post/tag/tips" title="tips" rel="tag">tips</a><br />

	<h4>Related posts</h4>
	<ul class="st-related-posts">
	<li><a href="http://www.fwolf.com/blog/post/448" title="Git 合并 patch 时的冲突处理一例 (2009-08-25)">Git 合并 patch 时的冲突处理一例</a> (0)</li>
	<li><a href="http://www.fwolf.com/blog/post/300" title="针对$_SERVER['PHP_SELF']的跨站脚本攻击（XSS） (2007-03-18)">针对$_SERVER['PHP_SELF']的跨站脚本攻击（XSS）</a> (3)</li>
	<li><a href="http://www.fwolf.com/blog/post/410" title="用ssh打通反向隧道，内网也可对外提供服务 (2008-07-10)">用ssh打通反向隧道，内网也可对外提供服务</a> (2)</li>
	<li><a href="http://www.fwolf.com/blog/post/127" title="利用SVN更新网站 (2006-01-19)">利用SVN更新网站</a> (7)</li>
	<li><a href="http://www.fwolf.com/blog/post/377" title="你最希望在哪里看到TIPS？ (2008-01-10)">你最希望在哪里看到TIPS？</a> (5)</li>
</ul>

]]></content:encoded>
			<wfw:commentRss>http://www.fwolf.com/blog/post/446/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>[Git]真正回滚已上传的更新</title>
		<link>http://www.fwolf.com/blog/post/441</link>
		<comments>http://www.fwolf.com/blog/post/441#comments</comments>
		<pubDate>Thu, 14 May 2009 10:32:50 +0000</pubDate>
		<dc:creator>Fwolf</dc:creator>
				<category><![CDATA[Svn/Git]]></category>
		<category><![CDATA[commit]]></category>
		<category><![CDATA[git]]></category>
		<category><![CDATA[reset]]></category>
		<category><![CDATA[revert]]></category>
		<category><![CDATA[rollback]]></category>
		<category><![CDATA[undo]]></category>

		<guid isPermaLink="false">http://www.fwolf.com/blog/post/441</guid>
		<description><![CDATA[首先，抛弃本地的修改应当用： $ git reset --hard HEAD 使用 git 自身功能来回滚代码，取消上一次的修改应该用： $ git revert sha1_of_commit 但要注意，虽然代码是实现了回滚，同时也会自动产生一条“回滚代码”的 log 。 有些时候，由于工作人员粗心，错误提交的内容完全无意义且占用空间颇大，就想真正 undo 掉错误的 commit，连历史记录都不想留。以下是我尝试的做法： 准备一份干净的客户端仓库 在客户端，下载服务器上的每个分支，并更新到最新状态。git branch -a查服务器上有哪些分支，挨个 checkout 过去再 pull。 然后回到有错误 commit 的分支，reset 掉错误的 commit： $ git reset --hard 671475b1ce 这样在客户端就形成了一个已剔除掉错误 commit 的完整状态了。 从客户端仓库生成服务端仓库 这就是 git 分布式源代码管理的优势，客户端也是完整仓库，只是表现形式与服务端的不同罢了，两者之间可以转换。在本地仓库( repo.client )的上级目录中执行： $ git clone --bare repo.client repo.git 然后把现在服务端仓库中的 hooks, info [...]]]></description>
			<content:encoded><![CDATA[<p>首先，抛弃本地的修改应当用：</p>

<pre><code>$ git reset --hard HEAD
</code></pre>

<p>使用 git 自身功能来回滚代码，取消上一次的修改应该用：</p>

<pre><code>$ git revert sha1_of_commit
</code></pre>

<p>但要注意，虽然代码是实现了回滚，同时也会自动产生一条“回滚代码”的 log 。</p>

<p>有些时候，由于工作人员粗心，错误提交的内容完全无意义且占用空间颇大，就想真正 undo 掉错误的 commit，连历史记录都不想留。以下是我尝试的做法：</p>

<h3>准备一份干净的客户端仓库</h3>

<p>在客户端，下载服务器上的每个分支，并更新到最新状态。<code>git branch -a</code>查服务器上有哪些分支，挨个 checkout 过去再 pull。</p>

<p>然后回到有错误 commit 的分支，reset 掉错误的 commit：</p>

<pre><code>$ git reset --hard 671475b1ce
</code></pre>

<p>这样在客户端就形成了一个已剔除掉错误 commit 的完整状态了。</p>

<h3>从客户端仓库生成服务端仓库</h3>

<p>这就是 git 分布式源代码管理的优势，客户端也是完整仓库，只是表现形式与服务端的不同罢了，两者之间可以转换。在本地仓库( repo.client )的上级目录中执行：</p>

<pre><code>$ git clone --bare repo.client repo.git
</code></pre>

<p>然后把现在服务端仓库中的 hooks, info 目录和 config, description 两个文件拷贝到新生成的服务端仓库当中。</p>

<p>然后备份旧的服务端仓库，删掉用新生成服务端仓库替代，并调整相关文件权限。</p>

<h3>基本上就可以了，小结</h3>

<p>在客户端 pull：</p>

<pre><code>$ git pull
From ssh://domain.tld/repo
 + 368b15f...671475b master     -&gt; origin/master  (forced update)
Already up-to-date.
</code></pre>

<p>客户端仓库的 HEAD 自动被重置到了错误 commit 之前的。</p>

<p>我这种做法，只适用于用户比较小，可以停掉服务慢慢弄的情况，并且会丢失所有错误 commit 之后的改动，所以要慎用。最好的方法还是搞好用户培训，避免产生离谱的错误提交，一些小的错误还是直接用revert好了。</p>

	Tags: <a href="http://www.fwolf.com/blog/post/tag/commit" title="commit" rel="tag">commit</a>, <a href="http://www.fwolf.com/blog/post/tag/git" title="git" rel="tag">git</a>, <a href="http://www.fwolf.com/blog/post/tag/reset" title="reset" rel="tag">reset</a>, <a href="http://www.fwolf.com/blog/post/tag/revert" title="revert" rel="tag">revert</a>, <a href="http://www.fwolf.com/blog/post/tag/rollback" title="rollback" rel="tag">rollback</a>, <a href="http://www.fwolf.com/blog/post/tag/undo" title="undo" rel="tag">undo</a><br />

	<h4>Related posts</h4>
	<ul class="st-related-posts">
	<li><a href="http://www.fwolf.com/blog/post/127" title="利用SVN更新网站 (2006-01-19)">利用SVN更新网站</a> (7)</li>
	<li><a href="http://www.fwolf.com/blog/post/431" title="[Git]提交后自动发email (2009-03-27)">[Git]提交后自动发email</a> (2)</li>
	<li><a href="http://www.fwolf.com/blog/post/446" title="[Git]初学者注意事项 (2009-08-04)">[Git]初学者注意事项</a> (0)</li>
	<li><a href="http://www.fwolf.com/blog/post/452" title="WordPress 烦人的 revision 和 auto-draft (2010-07-06)">WordPress 烦人的 revision 和 auto-draft</a> (1)</li>
	<li><a href="http://www.fwolf.com/blog/post/429" title="Git起步 (2009-03-26)">Git起步</a> (6)</li>
</ul>

]]></content:encoded>
			<wfw:commentRss>http://www.fwolf.com/blog/post/441/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>[Git]提交后自动发email</title>
		<link>http://www.fwolf.com/blog/post/431</link>
		<comments>http://www.fwolf.com/blog/post/431#comments</comments>
		<pubDate>Thu, 26 Mar 2009 18:37:57 +0000</pubDate>
		<dc:creator>Fwolf</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[Svn/Git]]></category>
		<category><![CDATA[email]]></category>
		<category><![CDATA[git]]></category>
		<category><![CDATA[gmail]]></category>
		<category><![CDATA[hooks]]></category>
		<category><![CDATA[msmtp]]></category>
		<category><![CDATA[tls]]></category>

		<guid isPermaLink="false">http://www.fwolf.com/blog/post/431</guid>
		<description><![CDATA[当然是通过hooks来实现了，对应post-receive，脚本也是已经随git-core安装就有了的：/usr/share/doc/git-core/contrib/hooks/post-receive-email，不过这个脚本用到了sendmail，我想好多机器上都没配这个东东吧，好在有好心人在这个脚本的基础上进行了完善，可以用msmtp发信了，看来我以前换用msmtp配mutt太正确了，另外msmtp也确实比sendmail小巧多了。 首先把刚才说到的脚本以及依赖文件functions一起下载到某地，加上执行属性（公用），然后ln到仓库的hooks目录下： $ cd test.git/hooks/ $ mv post-receive post-receive.origin $ ln -s ../../gittools/post-receive-email post-receive $ ln -s ../../gittools/functions 然后修改仓库里的config文件，注意不是客户端的： [hooks "post-receive-email"] mailinglist = list1@domain.tld, list2@domain.tld announcelist = envelopesender = mailsender@domain.tld sendmail = /usr/bin/msmtp 其中： mailinglist 默认的收信人，留空就不发信了 announcelist 创建tag时发送从上次创建tag以来的汇总邮件的收信人，比如用版本号作为tag的时候这就生成了每个版本的changelog，留空则用mailinglist的值 envelopesender 发信人/账号，要和msmtp的发信账号对应起来 sendmail sendmail或其它发信程序的路径 还有个环境变量$USER_EMAIL，也是个发信人，不过是显示在邮件From:里的。如果要设置邮件里的From:，需要自己修改post-receive-email或在其之前执行的脚本，设置环境变量： export USER_EMAIL="GIT &#60;mailsender@domain.tld&#62;" 这一版修改中作者还去掉了原来的emailprefix设置参数，固定为用仓库里description文件的内容加上[]替代。 最后，在git（git服务器运行用户）用户的HOME下放一个.msmtprc，配置发信认证信息： defaults tls_trust_file /etc/ssl/certs/ca-certificates.crt account gmail host smtp.gmail.com [...]]]></description>
			<content:encoded><![CDATA[<p>当然是通过hooks来实现了，对应post-receive，脚本也是已经随git-core安装就有了的：<code>/usr/share/doc/git-core/contrib/hooks/post-receive-email</code>，不过这个脚本用到了sendmail，我想好多机器上都没配这个东东吧，好在有好心人在这个脚本的基础上进行了<a href="http://github.com/stephenh/git-central/blob/master/server/post-receive-email">完善</a>，可以用msmtp发信了，看来我以前<a href="379">换用msmtp配mutt</a>太正确了，另外msmtp也确实比sendmail小巧多了。</p>

<p>首先把刚才说到的<a href="http://github.com/stephenh/git-central/blob/master/server/post-receive-email">脚本</a>以及依赖文件<a href="http://github.com/stephenh/git-central/blob/master/server/functions">functions</a>一起下载到某地，加上执行属性（公用），然后ln到仓库的hooks目录下：</p>

<pre><code>$ cd test.git/hooks/
$ mv post-receive post-receive.origin
$ ln -s ../../gittools/post-receive-email post-receive
$ ln -s ../../gittools/functions
</code></pre>

<p>然后修改仓库里的config文件，注意不是客户端的：</p>

<pre><code>[hooks "post-receive-email"]
    mailinglist = list1@domain.tld, list2@domain.tld
    announcelist =
    envelopesender = mailsender@domain.tld
    sendmail = /usr/bin/msmtp
</code></pre>

<p>其中：</p>

<ul>
<li>mailinglist 默认的收信人，留空就不发信了</li>
<li>announcelist 创建tag时发送从上次创建tag以来的汇总邮件的收信人，比如用版本号作为tag的时候这就生成了每个版本的changelog，留空则用mailinglist的值</li>
<li>envelopesender 发信人/账号，要和msmtp的发信账号对应起来</li>
<li>sendmail sendmail或其它发信程序的路径</li>
</ul>

<p>还有个环境变量<code>$USER_EMAIL</code>，也是个发信人，不过是显示在邮件<code>From:</code>里的。如果要设置邮件里的<code>From:</code>，需要自己修改post-receive-email或在其之前执行的脚本，设置环境变量：</p>

<pre><code>export USER_EMAIL="GIT &lt;mailsender@domain.tld&gt;"
</code></pre>

<p>这一版修改中作者还去掉了原来的<code>emailprefix</code>设置参数，固定为用仓库里<code>description</code>文件的内容加上<code>[]</code>替代。</p>

<p>最后，在git（git服务器运行用户）用户的HOME下放一个<code>.msmtprc</code>，配置发信认证信息：</p>

<pre><code>defaults
    tls_trust_file /etc/ssl/certs/ca-certificates.crt
account gmail
    host smtp.gmail.com
    port 465
    auth on
    tls on
    tls_starttls off
account mailsender@domain.tld : gmail
#   from must be same as From: in mail, exclude Name, only mail address
#   Because maybe msmtp select account using from
    from mailsender@domain.tld
    user "mailsender@domain.tld"
    password my_passwd
account default : mailsender@domain.tld
</code></pre>

<p>现在就可以测试push，看自动发信是否正常了。如果提示<code>functions</code>语法错误，比如：</p>

<pre><code>hooks/functions: 213: Syntax error: Bad for loop variable
</code></pre>

<p>可以把这两个脚本第一行的<code>#!/bin/sh</code>改为<code>#!/bin/bash</code>。</p>

<p>注意msmtp调用的是提交动作所属用户的<code>.msmtprc</code>，从本机提交和远程ssh进来可能用的就不是一个<code>.msmtprc</code>了。</p>

<h4>参考</h4>

<ul>
<li><a href="http://developer.berlios.de/docman/display_doc.php?docid=1813&amp;group_id=2#commit-email">Getting email from GIT</a></li>
<li><a href="http://www.rubick.com/blogger/one-entry?entry_id=30408">Setting up git commit emails</a></li>
<li><a href="http://ubuntuforums.org/archive/index.php/t-703467.html">help regarding for loop syntax in ubuntu 7.10</a></li>
</ul>

	Tags: <a href="http://www.fwolf.com/blog/post/tag/email" title="email" rel="tag">email</a>, <a href="http://www.fwolf.com/blog/post/tag/git" title="git" rel="tag">git</a>, <a href="http://www.fwolf.com/blog/post/tag/gmail" title="gmail" rel="tag">gmail</a>, <a href="http://www.fwolf.com/blog/post/tag/hooks" title="hooks" rel="tag">hooks</a>, <a href="http://www.fwolf.com/blog/post/tag/msmtp" title="msmtp" rel="tag">msmtp</a>, <a href="http://www.fwolf.com/blog/post/tag/tls" title="tls" rel="tag">tls</a><br />

	<h4>Related posts</h4>
	<ul class="st-related-posts">
	<li><a href="http://www.fwolf.com/blog/post/379" title="[Mutt]用msmtp替代esmtp作发信代理 (2008-01-12)">[Mutt]用msmtp替代esmtp作发信代理</a> (2)</li>
	<li><a href="http://www.fwolf.com/blog/post/429" title="Git起步 (2009-03-26)">Git起步</a> (6)</li>
	<li><a href="http://www.fwolf.com/blog/post/155" title="终于能够通过phpmailer使用gmail账号发送邮件了 (2006-04-14)">终于能够通过phpmailer使用gmail账号发送邮件了</a> (50)</li>
	<li><a href="http://www.fwolf.com/blog/post/127" title="利用SVN更新网站 (2006-01-19)">利用SVN更新网站</a> (7)</li>
	<li><a href="http://www.fwolf.com/blog/post/441" title="[Git]真正回滚已上传的更新 (2009-05-14)">[Git]真正回滚已上传的更新</a> (0)</li>
</ul>

]]></content:encoded>
			<wfw:commentRss>http://www.fwolf.com/blog/post/431/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>
