Push secret changesets - mercurial

That may look paradoxical, I know that secret changesets are meant to be private, but what if I want to backup those secret changesets?
I work with some branches in parallel and sometimes I want to push one, but not the others. To achieve that, I work in separate clones but I hate that.
So now mercurial has phases, I can make secret branches and have everything in the same repository. The problem is that between the beginning of the secret branch and its publication, I want to backup those secret changesets (I have a clone in another machine just to hold my backups in case something happens with my local repo or my machine).
Is there a way of doing that or my workflow is completly wrong?

No need to mark anything secret. If you only want to push one branch, use:
hg push -r REV
This will push REV and its ancestors only.
Secret is good for Mercurial patch queue revisions, since they can't be pushed anyway and it prevents a local clone from copying them.
Draft is good for tracking unpushed changes. If you still want to back them up, pushing them will flip them to Public, but you can reset them back to draft (as compared to another repository) with:
hg phase -fd 'outgoing(URL)'
(URL can be blank for the default push repo).

It seems like phases are still relatively new and some workflows, such as this, don't seem to be included, yet. As of 2013-03-19, I believe the only way you can do this is manually changing the phases from secret to public.
You can use these commands from the command line:
for /f "delims=" %a in ('hg log --template "{rev} " -r "secret()"') do #set secret=%a
hg phase -d %secret%
hg push -f
hg phase -sf %secret%
This doesn't change the commits to secret on the repository you are pushing to, I tried to change the push to do this (but was unsuccessful):
hg push -f --remotecmd hg phase -sf %secret%
The commits would have to match exactly for the remote hg command to work, but I couldn't get it to change on the remote repository anyway.
============================================================
If you want to use a GUI like TortoiseHG Workbench you will have to do this all manually (change the phases in the GUI on any repositories you want to) at the moment. Sorry, and hopefully, we can find a better solution, soon!

The best approach is a combination of #mischab1's answer, #mark-tolonen's answer and aliases.
By following mischab1's answer, you make sure that pushing to your backup location will not change the phase to "public".
Second step would be to add the backup location to your repository's hgrc/paths:
[paths]
default = ...
backup = backup_location
The next step is to define a backup command via alias in the global hgrc, e.g. "bubr" (for backup current branch) or "burev" (backup current rev).
[alias]
bubr = push -b . backup
burev = push -r . backup
hg bubr or hg burev will then push the current branch/revision to the location defined as "backup" path.
Edit This still has the drawback that you could accidentally push all changes with "hg push" so defining also an hg pubr command to push the current branch and not using "hg push" per default might be helpful.

This is the best I have come up with so far. I think its essentially equivalent to what you want push/pull to be able to do.
Mark all the secret changesets you want to transfer as draft
In the source repo run hg bundle -r last_draft_rev bundlefile.hg path\to\backup\repo
In the destination repo run hg unbundle bundlefile.hg
The changesets will come into the backup as DRAFT
Mark the first draft changeset as secret, and all its descendants will be marked thusly
I couldn't get #2 to work if the changesets were still marked secret.

#echo off
rem hgfullpull_naive.cmd
setlocal
set SRC_REPO=%~f1
set DST_REPO=%~f2
set TMP_DIR=%TEMP%\%~n0.tmp
set NODES_LIST=%TMP_DIR%\%~n0.%RANDOM%.tmp
if "%SRC_REPO%"=="" exit /b 1
if "%DST_REPO%"=="" exit /b 1
if "%SRC_REPO%"=="%DST_REPO%" exit /b 1
call :ALL
del /Q "%NODES_LIST%"
endlocal
goto :eof
:ALL
md "%TMP_DIR%"
hg log --rev "secret()" --template "{node}\n" --repository "%SRC_REPO%" >"%NODES_LIST%" || exit /b 1
call :CHANGE_PHASE "%SRC_REPO%" --draft
hg pull --repository "%DST_REPO%" "%SRC_REPO%"
call :CHANGE_PHASE "%SRC_REPO%" --secret
call :CHANGE_PHASE "%DST_REPO%" --secret
goto :eof
:CHANGE_PHASE
setlocal
set REPO=%~1
set PHASE=%~2
for /F "eol=; delims= usebackq" %%i IN ("%NODES_LIST%") DO (hg phase %PHASE% --force --rev %%i --repository "%REPO%")
endlocal
goto :eof

The easiest thing to do now-days, is to mark your backup repository as non-publishing by adding the following to its hgrc config file.
[phases]
publish = False
See Mercurial's Wiki for more info.

Related

How do I convert a Mercurial queues patch to local changes only?

This answer shows how you can demote a commit to a patch, but how can I convert an mq patch to local changes only?
Short answer
Make sure the patch is applied, then:
hg qrefresh nothing
hg qpop --keep-changes
hg qdelete "Name of patch"
Long answer
First, you need to make sure no changes are tracked by the patch. To do that, use
hg qrefresh nothing
nothing is just a random file name that is not in the repository. I usually use hg qref 0 for brevity. hg qrefresh accepts an optional file pattern. If it is given, the patch will track the changes that match the pattern - and only those. When nothing matches the file pattern, no changes will be tracked by the patch, and thus there will be local changes only.
Now you have a useless patch lying around, and you have some local changes. To clean up, you can do
hg qpop --keep-changes
to pop the patch even though there are local changes. Finally, to remove the dead, empty and unapplied patch you can use
hg qrm "Name of patch"
You can't remove an applied patch, which is why you need the hg qpop --keep-changes step.
(Note: hg qrm and hg qremove are aliases of hg qdelete.)
If using TortoiseHg
With TortoiseHg, exporting the patch to the clipboard (Workbench > right-click the patch > Export > Copy Patch), then unapplying the patch, and finally importing from the clipboard with the destination being "Working Directory" seems to work. Here are some screen captures demonstrating this procedure:
An arguably simpler alternative:
hg qfinish qtip
hg strip -k tip
That is, finish the patch and then remove the resulting commit while retaining its changes (the -k option to strip).

Discard all and get clean copy of latest revision?

I'm moving a build process to use mercurial and want to get the working directory back to the state of the tip revision. Earlier runs of the build process will have modified some files and added some files that I don't want to commit, so I have local changes and files that aren't added to the repository.
What's the easiest way to discard all that and get a clean working directory that has the latest revision?
Currently I'm doing this:
hg revert --all
<build command here to delete the contents of the working directory, except the .hg folder.>
hg pull
hg update -r MY_BRANCH
but it seems like there should be a simpler way.
I want to do the equivalent of deleting the repo, doing a fresh clone, and an update. But the repo is too big for that to be fast enough.
Those steps should be able to be shortened down to:
hg pull
hg update -r MY_BRANCH -C
The -C flag tells the update command to discard all local changes before updating.
However, this might still leave untracked files in your repository. It sounds like you want to get rid of those as well, so I would use the purge extension for that:
hg pull
hg update -r MY_BRANCH -C
hg purge
In any case, there is no single one command you can ask Mercurial to perform that will do everything you want here, except if you change the process to that "full clone" method that you say you can't do.
hg up -C
This will remove all the changes and update to the latest head in the current branch.
And you can turn on purge extension to be able to remove all unversioned files too.
To delete untracked on *nix without the purge extension you can use
hg pull
hg update -r MY_BRANCH -C
hg status -un|xargs rm
Which is using
update -r --rev REV revision
update -C --clean discard uncommitted changes (no backup)
status -u --unknown show only unknown (not tracked) files
status -n --no-status hide status prefix
hg status will show you all the new files, and then you can just rm them.
Normally I want to get rid of ignored and unversioned files, so:
hg status -iu # to show
hg status -iun0 | xargs -r0 rm # to destroy
And then follow that with:
hg update -C -r xxxxx
which puts all the versioned files in the right state for revision xxxx
To follow the Stack Overflow tradition of telling you that you don't want to do this, I often find that this "Nuclear Option" has destroyed stuff I care about.
The right way to do it is to have a 'make clean' option in your build process, and maybe a 'make reallyclean' and 'make distclean' too.
If you're looking for a method that's easy, then you might want to try this.
I for myself can hardly remember commandlines for all of my tools, so I tend to do it using the UI:
1. First, select "commit"
2. Then, display ignored files. If you have uncommitted changes, hide them.
3. Now, select all of them and click "Delete Unversioned".
Done. It's a procedure that is far easier to remember than commandline stuff.

Close multiple branches in TortoiseHg

I use TortoiseHg 1.1 and in our team we tend to create named branches for each case we work on then merge with default when we're done. Unfortunately most of the named branches don't get closed. This isn't a huge problem but it's annoying when you try and filter by the branch you are currently working on and there is a massive list of old branches to search through.
So is there an easy way in TortoiseHg or from the command line to close multiple branches quickly without having to manually commit and close on each branch?
Unfortunately no.
You need to close each branch separately with its own commit on that branch.
The best way to do that is to use the command line, you could even create a simple batch file to close a bunch of them:
for %%f in (branch1 branch2 branch4) do (
hg up "%%f"
if exitcode 1 goto quit
hg commit --close-branch -m "closing branch %%f"
if exitcode 1 goto quit
)
:quit
The workflow I use is to use the bookmarks extension to keep only local bookmarks for my lines of development, this way I use normal unnamed branches in Mercurial, but can still easily tell them apart locally. The commit messages are used to separate them later.
This way I don't have a bunch of named branches cluttering up my history, I don't have to remember to close them, and I only have to keep track of the branches I use locally.
See the following for all the possible options:
https://www.mercurial-scm.org/wiki/PruningDeadBranches
of which the closing branch option can be used.
hg up -C badbranch
hg commit --close-branch -m 'close badbranch, this approach never worked'
hg up -C default # switch back to "good" branch
Also, my understanding has been that it is preferable to clone for most work and use named branches only for few possible long lived trains of development.
Here is a powershell script that will close all active branches except the default.
Add your own filtering logic to exclude branches you don't want to close.
$branches = hg branches -a | sort
$exclude = "default"
foreach ($item in $branches)
{
$branch = $item.Split([string[]] " ", [StringSplitOptions]::RemoveEmptyEntries)[0]
if(!$exclude.Contains($branch))
{
hg update $branch
hg commit --close-branch -m "closing old branch"
hg status
Write-Host $branch
}
}
hg push
Okay so this is way late, but I set out to do this via bash and after a lot of pain I got a working solution. Take note that this does not do an hg push at the end, because I wanted to look over the results in tortoise before pushing.
#Protected branches to not close
develop='develop'
default='default'
IFS=$'\n'
#Get all branches
allBranches=$( hg branches | sort )
#Loop over and close old branches
for item in $allBranches
do
#Trim off everything but the branch name
branch=$( echo $item | sed -r 's/\s*[0-9]+:[0-9a-f]+(\s\(inactive\))?$//' )
if [ $branch != $develop ] && [ $branch != $default ]
then
hg update $branch
hg commit --close-branch -m "Closing old branch"
fi
done
After running the script I asked the team which branches they were actively working on and stripped those closure commits.

In Mercurial, after "hg init" to create a project and pushing onto server, how to make local directory to have "hg path" of the server?

When a project is started with
mkdir proj
cd proj
hg init
[create some files]
hg add file.txt
hg commit
hg push ssh://me#somewhere.somehost.com/proj
now when hg path is issued, nothing will show. How do we actually change the repository so that it is as if it is cloned from me#somewhere.somehost.com/proj ? Is it just by editing .hg/hgrc and adding
[paths]
default = ssh://me#somewhere.somehost.com/proj
because that feels like too low level an operation to do (by editing a text file)
It's the only way to do it in this situation. There are plenty of other cases where you have to edit the hgrc by hand, like setting up hooks or enabling extensions, so it's not as if it's unusual.
(As you probably already know, hg clone will set the path entry in the clone to point back to the original.)

Can't branch a single file with Mercurial?

is this possible with Mercurial? and which Version Control system can do this besides Clearcase?
David is correct that you can't have a branch that exists on only a single file, but it's worth pointing out that people often have branches that alter only a single file. Since the branch metadata is stored in the changeset, and since the changeset contains only a delta (change), having a branch that alters only a single files is nearly instantanous to create, update, commit, and merge, plus it takes up almost no space on disk.
Resultingly, it's a very common way to handle per-customer configurations. Keep the tiny change for them in a branch, and merge from main, where development happened, into that branch, whenever you want to update their deployment.
How you could use MQ:
$ hg qnew -m "Changes for client0" client0
... change the file ...
$ hg qref # update the client0 patch with the changes
$ hg qpop # pop the changes off the queue stack
... develop like normal ...
... client0 asks for a build ...
$ hg qpu # apply client0's patch
$ make release
$ hg qpop
It would get a bit finicky if you've got to deal with a lot of clients… But it may be worth considering.
The other thing you could do, of course, is just commit a bunch of .diff files:
... make changes for client 0 ...
$ hg diff > client0.diff
$ hg revert --all
$ hg add client0.diff
$ hg ci -m "Adding client0 changes"
... develop ...
... client0 asks for a build ...
$ patch -p1 < client0.diff
$ make release
$ hg revert --all
No, it's not possible. A branch in Mercurial is a snapshot of the entire repository state.
You could do it with CVS, though, as CVS tracks changes on a per-file basis :)