TE
科技回声
首页24小时热榜最新最佳问答展示工作
GitHubTwitter
首页

科技回声

基于 Next.js 构建的科技新闻平台,提供全球科技新闻和讨论内容。

GitHubTwitter

首页

首页最新最佳问答展示工作

资源链接

HackerNews API原版 HackerNewsNext.js

© 2025 科技回声. 版权所有。

Understanding the Git Workflow

378 点作者 RSkuja将近 14 年前

18 条评论

pilif将近 14 年前
The minute I learned about "rebase -i" and "add -p" has changed how I think about commits. I learned how I could easily keep the history clean and conversely, I learned the huge value that a clean history has for maintenance.<p>Now, building the commits as self-contained entities that don't break the build in between not only helps me while searching bugs later on, it sometimes helps me detect code smells around unneeded dependencies.<p>That said, I still like to merge big features with --no-ff if they change a lot of code and evolved over a long time, as that, again, helps keeping history clean because a reader can clearly distinguish code before the big change from code after the big change.<p>Of course the individual commits in the branch are still clean and readable, but the explicit merge still helps if you look at the history after some time.<p>"you said 'a long time in development' - surely the merge target has changed in between. Why still -no-ff?" you might ask.<p>The reason, again, is clean history: before merging I usually rebase on top of the merge target to remove eventual bitrot and in order to keep the merge commit clean. Having merge commits with huge code changes in them which we're caused by fixing merge conflicts, again, feels bad.<p>But this is certainly a matter of taste.
评论 #2831262 未加载
评论 #2828358 未加载
decklin将近 14 年前
The idea that fast-forward merges are easier to follow is subjective. I find my --no-ff history easier to read. This author doesn't.<p>What always using fast-forward merges <i>really</i> means is that you rebase each branch onto master once it's ready to be public. Therefore, instead of resolving conflicts when the branch is merged, the commits are rewritten to avoid introducing the conflict in the first place.<p>Sometimes, this is really simple -- I added a line in one spot, you added another line in the same spot, you merged first, so I rewrite my commit to add my line next to yours instead of merging and resolving the conflict. Sometimes, it's not -- maybe there's not even any text-level conflict, but your feature and my feature interact in subtle and unanticipated ways and something breaks. Now, there's no "good" point in my branch to refer to, because I rewrote it on top of something where (I didn't realize) it was never really going to work. The unit test I now need couldn't have existed because it involves things that, when I was developing the branch, didn't exist.<p>Rebasing first is trading off <i>when</i> you do that work. There's more to review when the branch is ready, and there's a stronger incentive to get it right the first time. I think this may work better for the "two founders deploying from master when they feel like it" scenario -- you pay for manageability with context switches. If you have a formal QA process, I think being able to distinguish between "this branch failed QA" and "the combination of these branches failed" may be more helpful -- you can parallelize work and hack on a different private branch.<p>Git, thankfully, does not force us to choose one model or the other :-)
评论 #2829485 未加载
sunchild将近 14 年前
This opened my eyes a bit. I am a walking, talking git anti-pattern today. I'm mostly on a two-man team, so I can get away with it. I'm definitely going to start thinking more about a clean history on master.<p>What are some other best-practice git workflows that HN readers use?
gruseom将近 14 年前
I work this way and agree about the value of a clean, linear history. It makes working with past versions of your code a breeze. There's one thing the OP doesn't mention that I've found important.<p>Say you're working on a major design change in a private branch and it has 100 commits. When it's ready to be put on top of master, you'd really like not to squash all 100 commits. Unfortunately, if there are conflicts, then rebasing B1,B2,...,B100 onto master is likely to be much harder than squashing B1,...,B99 into B100 and then rebasing. Why? In the squashed case you only have to deal with conflicts between B100 and master, while in the unsquashed case you have to deal with all the conflicts that ever existed as you progressed from B1 to B100. It's frustrating to find yourself fixing conflicts in code that you know doesn't exist any more. It's also error-prone since it forces you to remember what you were doing at all those steps. In such situations, I give up and squash. That's not great either, since you now have the disadvantages of a single monolithic commit.<p>The solution is to be diligent about rebasing B onto master as frequently as master changes, so B never has a chance to drift too far afield. This at least gets rid of the worst pain, which is conflicts that compounded unnecessarily. It also keeps you aware of what's happening on master.
评论 #2828707 未加载
pflanze将近 14 年前
I've always been an extensive user of rebase -i. Committing partial work often using git commit -a is easier, or at least takes less concentration, than always being careful to commit selectively with git add -p, git commit $files, but it needs squashing of those partial commits later on. I found that git rebase -i wouldn't scale to several days worth of work: I would frequently make errors when dealing with conflicts, and restarting rebase -i from scratch would mean redoing much of the work.<p>Because of this, I wrote a tool[1] that lets me do the same thing as git rebase -i, but allows me to edit the history changes incrementally, by keeping the history edit file and conflict resolutions around between runs; it does this by creating git patch files from all commits in question. I now always use this whenever I need to do more than one or two changes on some history; also, I'm now often creating commits to just store a note about a thought/idea/issue (the tool automatically adds a tag to the original history head, so I can look at those later on).<p>I originally wrote this just for me, which is the reason its own history isn't particularly clean and that I'm relying on a set of never-released libraries of mine; also maybe there are other, perhaps more well-known or polished tools than this, I don't know. I guess I should announce this on the Git mailing list to get feedback by the core devs.<p>[1] <a href="https://github.com/pflanze/cj-git-patchtool" rel="nofollow">https://github.com/pflanze/cj-git-patchtool</a><p>/plug
simonw将近 14 年前
This is the first argument for using rebase that I've found truly convincing - really worth reading. This will probably change the way I use git.
评论 #2828605 未加载
alunny将近 14 年前
For very short, "oh there's a syntax error I missed" commits, "commit --amend" is very useful, and quicker than "rebase -i".
评论 #2830162 未加载
评论 #2830160 未加载
zwieback将近 14 年前
Nice post, thanks.<p>I've been using traditional RCSs for years but find that whenever I introduce SVN (or CVS before that) to a team it's very easy for new users to fall into bad habits around branching and committing transitory changes.<p>I'd like to try git to help manage the mess during the prototyping phase but I'm wondering how suitable it is for new users to learn git vs. learning svn.<p>Any opions out there on the suitability of git as a first version control system? My team consists of highly experienced engineers (EE/FW) with little or no software engineering experience.
评论 #2828422 未加载
评论 #2830007 未加载
andrew311将近 14 年前
I'm wondering how people address one of the scenarios raised in the post, specifically this:<p>"It’s safest to keep private branches local. If you do need to push one, maybe to synchronize your work and home computers, tell your teammates that the branch you pushed is private so they don’t base work off of it.<p>You should never merge a private branch directly into a public branch with a vanilla merge. First, clean up your branch with tools like reset, rebase, squash merges, and commit amending."<p>I'm wonder how people address cleaning a private branch that has been pushed (when your goal is to get its changes into master cleanly). Rebasing the private branch is pretty much out of the picture since it has been pushed (unless you don't care about pushing it again). I can see some ways of doing this:<p>1) You could do a diff patch and apply it master, then commit.<p>2) You could checkout your private feature branch, do a git reset to master in such a way that your index is still from the private, then commit it. Ex:<p>currently on private branch git reset --soft master<p>Now all the changes from the private branch are changes to be committed on master. This is easy, but it puts everything in one commit.<p>If you wanted to do a few commits for different, but stable points, but you already pushed the private branch and can't rebase it, you could instead do "git reset --soft" on successive points in the private branch commit chain, committing to master as you go.<p>If you wanted to reorder commits from the private branch, I guess you could rebase the private branch (which means you can't push again since you pushed it already), then do the tactic from the last paragraph, then ditch the private branch cause it's no longer pushable.<p>Does anyone have better ways of putting changes to master for private branches that have already been pushed?
评论 #2832784 未加载
motherwell将近 14 年前
<a href="https://github.com/nvie/gitflow" rel="nofollow">https://github.com/nvie/gitflow</a> works really well. The original post <a href="http://nvie.com/posts/a-successful-git-branching-model/" rel="nofollow">http://nvie.com/posts/a-successful-git-branching-model/</a> was really compelling, and using it has really helped, at least what I do.
stretchwithme将近 14 年前
What really helped me grasp git was attending one of Scott Chacon's speeches on the topic. Scott works for github, knows what he's talking about and explains things thoroughly.<p><pre><code> http://www.youtube.com/watch?v=QF_OlomyKQQ</code></pre>
joelhaasnoot将近 14 年前
Hmm, this makes sense to me: lots of Git features I'd forgotten or not used before.<p>Can anyone sketch my "merging" strategy I should be using in my scenario: - Have 3 branches dev, stage and master - Bugs are fixed on master, bigger bugs/changes on stage and new features on dev - Big functionality changes/additions come in the form of new branches, which currently I first merge with dev, then with stage and if everything is OK, with master. This doesn't always work well due to the timing of things: sometimes my dev branch is out of date with the master and needs fixes from the master before applying.<p>How should I handle merging the branches?
评论 #2828980 未加载
评论 #2830156 未加载
Maro将近 14 年前
Great post. Calls attention to the importance of having clean, stable commits in the 'master' branch and thus avoiding plain vanilla 'git merge' for 'squash' and 'rebase'.<p><a href="http://stackoverflow.com/questions/2427238/in-git-what-is-the-difference-between-merge-squash-and-rebase" rel="nofollow">http://stackoverflow.com/questions/2427238/in-git-what-is-th...</a>
swah将近 14 年前
He should start the article with the last paragraph.
评论 #2829514 未加载
echostar将近 14 年前
Under "Declaring Branch Bankruptcy", why does the author throw in a "git reset" as the last step in the example.
endlessvoid94将近 14 年前
After reading this, I finally motivated myself to read through the man pages for git pull, fetch, merge, and rebase.<p>Thanks :-)
trusko将近 14 年前
Good article. Thanks
jebblue将近 14 年前
Git is plain scary. We should stick with SVN.
评论 #2830635 未加载
评论 #2830212 未加载