I'm trying to set up version control for a programming-related tutorial. It's proving problematic because there are two different kinds of history:
There's the history of the project being built by the tutorial, which is available for each chapter and is what the reader will see. If I never planned to change already-written chapters of the tutorial again, I could just store each chapter as a tag in the history of the project.
Then there's also the history of the tutorial itself (not only the text, but my working on the pretend history of the project). If I find a bug I need to go back and fix in chapter 1, adding a new commit to the end doesn't work because I want to change how the project "appeared" at that stage, i.e. insert a commit in the project history and move the chapter's tag forward.
So far I've thought about a few possibilities- using git branches where each chapter is a branch that gets rebased to the front of the previous chapter whenever I make a change, a mercurial patch queue that I insert patches into, or structuring the tutorial around a set of modules that I could put in subrepositories.
I thought I'd ask if anyone has experience with this kind of thing and what solutions worked and didn't.
Rather than rewriting the history of the all project because of a late fix to an early chapter, I would rather isolate each chapter in its own branch, have each HEAD representing the current state for each chapter.
Assembling the all tutorial is then more a release management issue (deploying your tutorial by extracting the relevant informations from the Git Repo).
You can then develop your tutorial to achieve something similar to git immersion.
(Note: If this was more an ebook you were after, then git-scribe would have been a more interesting way to version it.)
The OP rusky adds in the comments:
I'm trying to version the sample code for the chapters, where each chapter's code is based on the previous chapter's code
That means any bugfix you add needs to be reported to the other branches representing the other chapters, in which case see:
In Git, how do you apply a commit of bug fix to the other newer branches? (avoiding cherry-picking, which is generally a bad idea)
using a topic branch
rebase --onto solution
git rebase --interactive is probably the most straightforward solution here. That will let you choose a specific commit to edit, then reapply all the subsequent commits on top of it. Shouldn't be much more difficult than a regular merge, depending on how extensive your change is, of course. Check out the part of the git rebase man page on splitting commits. That will let you keep your original version for historical reasons, then add a new commit just after it in the history where you can move your tag.
The great thing about CLI-based version control is you can use shell scripts to build up tutorial examples, something like:
#!/bin/bash
mkdir example_repo
cd example_repo
git init .
touch file1
git add file1
git commit -m "Added file 1"
git checkout -b branch2
echo "New line" > file1
git commit -am "Added new line to file 1"
You get the idea. It lets you build up a new repo from scratch to whatever point you like, and mistakes in the middle are easy to fix because you start from a blank slate every time. You can put the shell script itself under version control, or just comment out parts you don't need for different examples.
This is what tags are for. Tag your "snapshot" points, and go from there. If you need to go back and change something, do so and update the tag. And if you don't want people to see the in-between stages, simply create a second repository and incrementally check in your commits one tag at a time.
Related
I am using git on VS2019
I was wondering if there is a way to remove old commits but always keep the latest code. The reason is that when I work on a big change, I do a lot of commits after checking every small step but at the end it is annoying when I click on view history and need to search an old commit to compare the changes. I would like to remove specific commits but leave the changes. In that way I will see on history only the main milestones of the developing process. There is a way to do that??
It sounds like in these scenarios, you're developing a new feature, in which case you should be using a branch+pull request approach to development. During the pull request, you can do a squash merge. It's not all in the command line, but that might be for the best anyway.
If you're already working within a feature branch, then I like VonC's approach.
It seems that you want the git rebase feature.
If you know how many commits you want to squash together, you can use
git rebase -i HEAD~N
You can also do it in interactive mode:
git rebase --interactive HEAD~N
Be aware that if you merging commits you have already pushed, you may have trouble with your next push.
For more details, see: https://www.internalpointers.com/post/squash-commits-into-one-git
You can follow a workflow similar to "Git better with fixups " from Atul Sharma
For the commits which are incremental improvement for the same step, use git commit --fixup small step, which allows you to "do a lot of commits after checking every small step".
(You can see it in Git Extension in Visual Studio)
But at the end, you can cleanup all those small steps with git rebase --interactive --autosquash <First step commit> (assuming you have not pushed those commits yet)
That way, you end up with a cleaner history.
Is there a way to either scroll faster through the commit history OR go to the very beginning?
I don’t see an option, not even on desktop site.
I’m trying to figure out why a certain branch was made but that info is not in the README section because the README is not customized per branch.
I don't think github has this built in but there are a couple of ways using URLs.
With some trial and error using page? gets you to the beginning of the history fast: https://github.com/micropython/micropython/commits/master?page=242
This is less trial and error because you can enter nearly exact values: you know the current latest commit's hash and github says there's about 8450 commits so this one gets you to the first page: https://github.com/micropython/micropython/commits/master?after=cada971113e6db0cf9e0751e95dbe9217dd707b5+8420
There used to be a tool for it but the site is dead now so only the code is left.
But anyway, I'm fairly sure you'd achieve your actual goal way (examining log searching for something) faster without github, by using git itself. Github is fine for hosting but it's not exactly a complete git user interface. On th other hand git log/blame/rev-list commands are built for that. E.g. first commit hash: git rev-list --max-parents=0 HEAD
The instructions for the 'advance testing setup' at the end of chapter 3 (3.7) of the book online suggest to checkout to the master branch before making changes.
Would not be convenient to add and commit these changes with git add -A and git commit -m? I am wondering whether there is some reason for these commands to be omitted at this stage, or they are supposed to be implicit.
No there where no good reasons for omitting them and either he forget about them or implies them, so use them as long as you can;)
I think Hartl states somewhere near the beginning of Chapter 3 that from then on, he will omit the git commit/git add steps from the text for simplicity, but that using them as he directed in the previous chapters is his recommended method for working.
I'm fairly new to Mercurial, but one of the advantages I see using Mercurial is that while writing a feature you can be more free to experiment, check in changes, share them, etc, while still maintaining a "clean" repo for the finished feature.
The issue is one of history. If I tried 6 different ways to get something to work, now I'm stuck with all of the history for all my mistakes. What I'd like to do is go through and clean up my changes and "collapse" them into one changeset that can be pushed into a shared repository. This is complicated by the fact that I might pull in new changesets from the shared repository, and have those changesets intermingled with my own.
The best way I know of to do that is to use hg export to create a patch of my changes since cloning, clone a fresh repository, and apply the patch to the fresh repository.
Those steps seems a little bit cumbersome and easy to mess up, particularly if this methodology is rolled out to the whole dev team, some of whom are a little resistant to change (don't get me started). TortoiseHg makes the process slightly better since you can highlight the changesets you want to be included in an export.
My question is this: Am I making this more complex than it needs to be? Is there a better workflow I can use to ease my troubles? Is it too much to expect a clean history where entire (small-ish) features are included in one changeset?
Or maybe my whole question could be summed up this way:
Is there an equivalent for this in mercurial? Collapsing a git repository's history
Although I think you should reconsider your use of branches in Mercurial (as per my comment on your post), using named branches doesn't really help with your concern of maintaining useless or unnecessary history - it just organizes them a bit.
I would recommend a combination of these tools:
mercurial queues
histedit (not distributed with Hg)
the mq changeset strip feature
to rework a messy history before pushing to a blessed or master repo. The easiest thing would be to use strip to permanently remove any changeset with no children. Once you've done that you can use mq or histedit to combine, relocate, or modify existing commits. Histedit will even let you redo the comment associated with a changeset.
Some pitfalls:
In your opening paragraph you mention sharing changesets during feature development. Please understand that once you've shared a changeset it's not a good idea to modify using mq or histedit, or strip. Using these extensions can result in a change to the revision hash, which will make them look like a new changeset to everyone else.
Also, I agree with Paul Nathan's comment that mq (and histedit) are power features and can easily destroy a history. It's a good idea to make a safety clone before using these extensions.
Named branches are the simplest solution. Each experimental approach gets its own branch.This retains the history of the experiments.
The next solution is to have a fresh clone for each experiment. The working one gets pushed back to the main repo.
The next solution - and probably what you are really looking for - is the mq extension, which can "squash" a series of patches into a single commit. I consider mq to be "advanced", and "subject to accidently shooting yourself in the foot". I also don't care to squash my commits - I like having my version history present for reference.
I'm using svn for the first time, to maintain a custom version of Wordpress. I'm using the subclipse plugin in eclipse. The time has come to merge the changes in the latest release of Wordpress with my customised code base.
I have tried creating a branch and adding the new Wordpress release there, then performing a merge. No changes were made however.
Could someone walk me through the setup of project like this? I fear I am missing something basic.
Thanks.
This is assuming you merge from branch (containing the latest version of Wordpress) to trunk (your customized codebase).
(Make sure that you have committed everything you need into branch.)
Team --> Switch to another branch/tag/revision... your working copy to trunk (the target of your merge operation), and resolve any conflicts that come up at this point.
Team --> Merge opens a dialog where you will be performing the merge operation. Change the "From" URL to reference branch (the source of your merge operation, i.e. what you want to merge into your working copy). "From Revision" should point to the revision in branch where you want your merge operation to "start" from - typically the revision that was last merged in from branch to trunk (or most likely the head revision in your case, if you really want to merge just that latest changes in branch).
Set "To Revision" to point to the latest revision in branch (= the head revision).
At this point you are ready to perform the merge - Dry run command lets you preview what will happen during the merge, and Merge will perform the actual merge.
Once the merge operation has been completed, you need to make sure that all changes that were performed against your working copy are ok, and resolve all conflicts.
When you're done with resolving conflicts and reviewing the changes, commit the changes to trunk in a single commit operation. For your own convenience, it is strongly recommended that you add a commit message where you specifically state what this commit is for ( = merging revisions from X to Y from branch to trunk, what was the purpose, etc.).
Hope this helps.
Converting wordpress project to vendor branch procedure
If you are using svn for the first time I suppose you have not started with a clean wordpress copy, branched from there and edited the branched version, have you? ;)
If that is so you might have a problem at your hands.
Background
Unlike "regular diffs" SVN merge does not compare right-side code/folders with left-side code/folders.
While svn merge might fall back to a diff-like mechanism if it does not find a history, I would not recommend relying on that as it can be quite prone to unneccessary conflicts.
SVN Merge is used to reproduce changes that have been recorded in the SVN history. It is like telling a painter "Hey you know how this picture looked before you added that tree on the hill? That Tree was great! Look here i've copied the same base picture but now it's with a sunset. Can you paint the same tree again but on this picture with the sunset?"
The painter might be able to reproduce the tree because he knows how he had done it. He might even have a draft somewhere.
The picture, that is wordpress. The painter, its svn with you commanding it. The tree thats your modifications. The picture now sunset-themed is the newer wordpress version.
What most likely you did is copy wordpress vanilla into your svn, modify it, work with it.
To stick with the picture example, the history would contain commands like "copy whole picture, add tree, add leaves".
Now you bring a new version of wordpress, a new picture so to say and put it besides your older modified version. The Problem is, you human and smart know its quite much the same picture and even though the newer verison is different you just have to copy the tree, SVN does not have that knowledge. For SVN your wordpress 1.7 folder (modified) is completly distinct from wordpress 1.8. They share no history because nothing in SVNs log indicate it. SVN is a bureaucratic snob isn't it? ;)
Now what people do to allow svn to maintain that historic connection between wordpress 1.7, your modified 1.7 and the new 1.8 is they use branching right at the beginning of their works.
So you would start off with a clean 1.7 wordpress in a "vanilla-wordpress" folder, store it in svn and branch it to, say, "my-modified-wp". There you hack away until you feel like updating wordpress from upstream. People then download the latest wordpress copy overwrite their vanilla wordpress and merge the resulting changeset.
In the picture example commands would be these:
"Buy original picture
copy original picture as my picture
draw tree on my picture
draw sunset on original picture (someone else did that for you, aka update)
*reproduce* sunset on my picture too"
You can cleanly reproduce the sunset because you know how the picture looked before the sunset was applied.
Your problem though is that you did not start that way off but edited on your downloaded wordpress right away. So your newer copy of wordpress can not be easily associated with your modified version.
One way to establish history relations
download the **exact** wordpress version you started your project with
Put it into /vendor/wordpress/current
invoke "svn copy http://svnserver.tld/repositorypath/vendor/wordpress/current http://svnserver.tld/repositorypath/vendor/wordpress/1.7.1" to tag the import.
invoke "svn copy http://svnserver.tld/repositorypath/vendor/wordpress/current http://svnserver.tld/repositorypath/branches/my-new-modified-wordpress" or whatever your project/WP-edition is called.
Now comes the trick part
Scroll back the svn log of your "old-modified-wordpress". The one that you did not branch. You have to find the first revision AFTER your initial import of the old wordpress. Once you found that revision you take its number and use it in the second of these two commands:
change into a local checkout of "/branches/my-new-modified-wordpress"
issue "svn merge -r **4**:HEAD http://svnserver.tld/repositorypath/my-**old**-modified-wordpress". If 4 was the first revision during which you made own modifications.
You are telling svn the following: "Take all changes in my old branch between revision 4 and NOW and reproduce them on my new branch."
If all works out you should have two identical branches. the old-modified and the new-modified with the slight difference that the new-modified has a solid history with your "/vendor/wordpress/current" branch.
This ancestry allows you to contunously do the following:
Download the wordpress version you wish to upgrade too and **overwrite** /vendor/wordpress/current
invoke "svn copy http://svnserver.tld/repositorypath/vendor/wordpress/current http://svnserver.tld/repositorypath/vendor/wordpress/1.9.3" to tag the new version.
change into local checkout /branches/my-new-modified-wordpress
issue "svn merge http://svnserver.tld/repositorypath/vendor/wordpress/current"
profit
This procedure I describe with less storystelling at the link allready. But before it can work you have to establish the ancestry relation between the branches.
Subversion svn:externals file override?
I know it has been alot to read :). If you plan do soem drawing, think of "change commands" not states and you'll be fine.
C