2013-06-24 19 views
2

状态:提出合并请求时,我希望接收者能够了解它做了哪些更改。我发现,他们压扁成一个承诺可能会造成混淆,尤其是如果:如何准备清晰的差异,当`git add --patch`编辑很尴尬?

  1. 有编辑代码也被移动 - 差异呈现此批发删除和添加,没有突出的编辑。

  2. 代码被添加到一系列相似的部分,例如, case语句,级联ifs,yacc productions - diff通常会将重叠部分重构为重叠部分(例如,不是添加部分,而是使用上一部分的开始,添加新结尾和另一个开始,然后使用前一部分的结束);添加新的codean结尾,并在某些情况下挑出一些小的相似性,然后删除并插入大量相同的代码。 (我认识到diff使用LCS并且速度惊人 - 但有时候它的结果很难理解,即使考虑到diff也不是语法感知的,并且无法识别您所看到的代码段)。

BTW:我用git diff --color-words --ignore-space-change,这是伟大的,也是错误来重构,可以隐藏的细节 - 而我关注的是,收件人可能使用纯git diff,看到完全不同的东西(他们可以重建不同) 。

TASK:好的,所以对此的明显解决方案是将合并请求分成不同的提交。有时候,这些可能是我开始使用的实际提交,所以我所需要做的并不是首先放置底座。但我发现即使那样,差异可能不清楚(特别是因为上面的原因(2)),我需要进一步将它们分开。

  1. 明显的做法是使用git add --patch/-p。但是,修补程序很难适用于重叠更改 - 您可以使用divide and even edit the hunks,但在您想要的更改包括添加,删除和通用代码时,通过反转差异来思考某种思维方式。

  2. 我实际上做的是直接编辑文件:删除我不想要的部分并提交;然后撤销删除操作(使用我的编辑器)并提交。在实际来源方面的工作比差异化工作更清晰,更直观 - 但感觉就像我在与Git战斗并做错了事(另外,看起来事故倾向于依靠编辑器撤消)。

  3. 它发生在我身上,而不是第一个git stash该文件,并通过删除我不想要的部分来准备第一次提交;然后git stash apply“撤销”删除准备第二次提交。但我不确定你可以在rebase的中间做到这一点(还没有尝试过)。

问题:它带我小时所有做这个...我想我会改善与实践,但... 我在正确的轨道上?有没有更好的办法?你能否首先防止误重构差异?我为了清晰而努力工作吗? (公平地说,这是对前一段时间细微而复杂的代码的许多编辑 - 花费这些时间显示出更深刻的见解。)

回答

2

基于these answers,开始一个互动变基(get rebase -i ...)和e谛听后一个承诺:

git reset HEAD^  # reverts index to previous commit (not change files) 
        # so it's as if you are just about to add and commit 
git stash   # save 
git stash apply  # get it back 
...edit the file, deleting the changes you don't want in the first commit 
git add . 
git commit -m "...first changes..." 

git stash apply  # get it back again (ie undo the above delete) 
...(I needed to resolve a merge conflict) 
git add . 
git commit -m "...second changes..." 

git rebase --continue 

可惜没有一个git stash copy,节省您的更改没有回复。可能会有更顺利的方式来做到这一点。

对我来说令人惊讶的是,你可以在交互式底座中使用git 的全部功能。您可以忽略您“应该”编辑的旧提交,而是添加两个提交;你可以隐藏并应用。我可能需要研究rebase实际上是如何实现的,并停止将其视为抽象。实际上,这个rebase页面的标题为splitting commits

1

为什么不使用提交撤消更改(而不是隐藏),因为我们已经有了它们?有两个问题:引用提交,并使文件(工作树)和索引处于正确的状态。

  1. 引用提交 我们可以剪切和粘贴提交的哈希值。或者,创建一个临时标签,git tag tmp(使用git tag -d tmp删除)。或者,从分支中统计提交n并使用branch~n。或者,对于rebase现在正在修改的提交,请使用它存储的散列,并使用cat .git/rebase-merge/amend(但很尴尬且没有文档实现细节 - 我获得了信息here)。

  2. 文件和索引 我目前的理解:resetcheckout不会改变HEAD当您指定的文件(路径)。当这样使用时,reset只改变索引; checkout更改索引和文件。要仅更改文件,可以用git show <commit>:file > file(注意文件的奇数:语法而不是--)将其打开。

将其组合在一起:

git checkout -b newbranch # I'm on a dev branch already; make a new one 
git rebase -i master  # only the commits not part of master 
...mark one with `edit` or `e`... 

git tag tmp  
git reset HEAD^   # changes index only, as if we had just edited 
...edit myfile, deleting what is to be split into another commit... 
git add . 
git commit -m "first commit" 

git tag tmp2 
git checkout tmp -- myfile # get file and index before above edit 
git reset tmp2    # ...so need to reset *index* to first commit 
          # 1. index is same as "first commit" 
          # 2. file is same as commit we wanted to split 
          # (the diff is what we deleted above) 
git add . 
git commit -m "second commit" 

git rebase --continue 
git tag -d tmp tmp2  # clean up 

第二个承诺是稍微简单一些,如果我们使用 '混帐秀',因为我们不需要git reset tmp2

git show tmp:myfile > myfile # clobber file, but not index 
git add . 
git commit -m "second commit" 

很难告诉这一切发生了什么!一些方法来检查当前的状态:

git log -1     # see HEAD 
git diff     # between files and index 
git diff --cached HEAD  # between index and HEAD 
git show-ref tmp   # see tag 

无论如何,这一切似乎更为复杂不仅仅是*撤消*我的编辑,我在第一时间做了内荷兰国际集团。 但我敢打赌,这个更好理解resetcheckout会派上用场......