* Re: [PATCH] run-command.c: Accept EACCES as command not found
From: Frans Klaver @ 2011-11-21 23:06 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
In-Reply-To: <7vbos5f7ix.fsf@alter.siamese.dyndns.org>
On Mon, 21 Nov 2011 23:13:58 +0100, Junio C Hamano <gitster@pobox.com>
wrote:
> Frans Klaver <fransklaver@gmail.com> writes:
>
>> execvp returns ENOENT if a command was not found after searching PATH.
>> If path contains a directory that current user has insufficient
>> privileges to, EACCES is returned. This may still mean the program
>> wasn't found.
>>
>> If the latter case is encountered, git errors out without giving aliases
>> a try,...
>
> Isn't that a *good* thing in general, though, so that the user can
> diagnose the breakage in the $PATH and fix it?
Actually I went through diagnosing and fixing it. After tracking it down,
I did wonder about this question myself and I didn't come to a definitive
conclusion on it. On one hand I do agree that it may be an incentive for
the user to fix his path. On the other hand I found it an obscure one to
track down; git's behavior doesn't match bash behavior:
$ git config --global alias.aliasedinit init &&
mkdir searchpath && chmod 400 searchpath && PATH=$(pwd)/searchpath:$PATH
&& export PATH &&
mkdir someproject && cd someproject &&
git aliasedinit
fatal: cannot exec 'git-aliasedinit': Permission denied
$ git-aliasedinit
bash: git-aliasedinit: command not found
This isn't very intuitive to track down an incorrect PATH with, imo. You
have to dig into git core code, learn about how git handles commands,
learn about debugging forked processes, and find out that execvp uses
EACCES for more than just "permission denied" _just_ to find out you've
got a wrong environment variable lying about. That's a full day of work
gone for a newbie. If bash would also tell me in natural language that
permission was denied, I wouldn't even have considered doing this patch.
For my part the root of the problem lies with the use of EACCES by execvp
here, not so much with how git uses it. For this particular case, EACCES
doesn't just mean "it exists, but you cannot execute this", it may also
mean "not found, but one of the paths could not be accessed". If git were
to provide a really helpful message, we'd have to detect which paths got
denied. Once we know that, we can even on the spot decide to error out or
not. In other words, we'd have to figure out which meaning of EACCES is
actually used. Based on that, git can error out, warn or ignore at will.
In any case, I thought it best to have other developers have a look at it.
I can put a bit more of that information in the commit message, but I'd be
just as happy to drop the patch and keep the exercise.
Frans
^ permalink raw reply
* Re: [RFC/PATCH] remote: add new sync command
From: Felipe Contreras @ 2011-11-21 23:47 UTC (permalink / raw)
To: Jeff King; +Cc: git
In-Reply-To: <20111121214450.GA20338@sigill.intra.peff.net>
On Mon, Nov 21, 2011 at 11:44 PM, Jeff King <peff@peff.net> wrote:
> On Mon, Nov 14, 2011 at 03:57:07PM +0200, Felipe Contreras wrote:
>
>> >> I'm not going to investigate the subtleties of these different setups,
>> >> I'm going to put my common user hat and ask; how do I fetch as a
>> >> mirror?
>> >
>> > The problem with that question is that you haven't defined mirror. Does
>> > that mean you just want pruning, or does it mean that you want your
>> > local ref namespace to match that of the remote?
>>
>> Exactly, no mirror has been defined, because I don't want a mirror. A
>> mirror is supposed to have all the refs in sync all the time; that's
>> not what I want.
>
> I didn't mean "you didn't define a mirror in your config". I meant "your
> question is not well-defined, because you haven't defined the term
> 'mirror'". IOW, I can't answer your question without knowing exactly
> what you meant.
I wasn't the one that brought the mirror up, you did:
> I think --prune wouldn't need renaming. If you fetch into tracking
> branches, then pruning would prune tracking branches. If you fetch as a
> mirror (refs/*:refs/*), then you would prune everything.
So you should know what you meant :)
>> > BTW, right now there is "git remote add --mirror ...", which sets up the
>> > fetch refspec for you (in this case, mirror is "make your refs look like
>> > the remote's"). Perhaps rather than adding syntactic sugar to fetch, it
>> > would be best to channel users into configuring a remote that selects
>> > from one of a few common setups (including different types of mirrors).
>>
>> But that assumes that they would want the same refspec operation *all
>> the time* which is not the case (at least for me). Sometimes I want to
>> update only existing branches, sometimes I want to fetch new branches
>> too, sometimes I want to prune local branches, sometimes not.
>
> OK, then that means it must be a fetch command-line thing, not a
> configured thing. Though note that even leaving prune out, I don't think
> git does what you want (e.g., how are you fetching only to update
> existing branches?).
It should be possible to do a git fetch, so the remote tracking
branches are updated, and then compare those with the local ones. This
might not feel natural under 'git fetch' which is why I decided to use
'git remote sync'.
>> > No, you would just do "--prune", because your refspecs are _already_
>> > indicating that you are writing into the local namespace, and anything
>> > you have locally would be deleted by the prune operation. I.e., there is
>> > no need for --prune-local in this scenario; --prune already does what we
>> > want.
>>
>> That's very risky. The user might forget that this is a mirror repo,
>> and delete the local branches unintentionally. Plus, it would be then
>> impossible to prune remote tracking branches.
>
> Sorry, but I don't see how "--prune" is supposed to know what to prune
> except through the refspecs that have been provided to it (either in
> configuration or on the command line). So what is:
That's why I suggested --prune-local.
> git fetch --prune <remote> refs/*:refs/*
>
> _supposed_ to do, if not prune your local namespace?
I thought 'git fetch --prune <remote_mirror>' would not prune the
local branches, but now I see that it does (currently), so nevermind.
>> > As a user, how do I resolve the situation? I might say topic-Y is
>> > obsolete and get rid of it. I might rebase it onto another branch. Or I
>> > might declare it to have no upstream. But all of those are branch
>> > operations, not fetch operations.
>>
>> Yes, but that has nothing to do with the operation I want to achieve:
>> git remote sync. By which I mean synchronize the local branches with
>> the branches of a certain remote.
>
> Right. I was only trying to explain a case where you would want to prune
> in the local namespace, when fetch is not configured to touch the local
> namespace. Which is the only use case I could think of for something
> named --prune-local. But let's forget it. My point was that it is not
> related to fetch, and I was just guessing at what you might want from
> --prune-local.
Basically 'git fetch --prune <remote> refs/*:refs/*', when the remote
is not configured as a mirror. But it would be nice to prune the
branches without having to update the local ones (for whatever
reason).
>> > So what I was trying to say was that either your fetch refspecs tell
>> > fetch to write into your local branch namespace, or not. If they do,
>> > then --prune is sufficient (with no -local variant required). If not,
>> > then touching your local branch namespace is outside the scope of fetch.
>>
>> I don't want this to be a *permanent* configuration. I see this
>> similar to --force. You can achieve the same by adding a + at the
>> beginning of the refspec, but this is something that should be
>> activated on a per-command basis, thus the option. I think this should
>> be the same.
>
> Then you can tweak what is pruned on a per-command basis by providing
> alternate refspecs. Right now that would probably mean:
>
> git fetch --prune <remote> refs/*:refs/*
>
> or
>
> git fetch --prune <remote> refs/heads/*:refs/remotes/<remote>/*
>
> but as we discussed earlier in the thread, those can be made less scary
> with syntactic sugar.
Indeed, but those commands will also update the local branches.
--
Felipe Contreras
^ permalink raw reply
* Re: [PATCH] run-command.c: Accept EACCES as command not found
From: Junio C Hamano @ 2011-11-21 23:54 UTC (permalink / raw)
To: Frans Klaver; +Cc: git
In-Reply-To: <op.v5bjtk1r0aolir@keputer>
"Frans Klaver" <fransklaver@gmail.com> writes:
> Actually I went through diagnosing and fixing it. After tracking it
> down, I did wonder about this question myself and I didn't come to a
> definitive conclusion on it. On one hand I do agree that it may be an
> incentive for the user to fix his path. On the other hand I found it
> an obscure one to track down; git's behavior doesn't match bash
> behavior:
>
> $ git config --global alias.aliasedinit init &&
> mkdir searchpath && chmod 400 searchpath &&
> PATH=$(pwd)/searchpath:$PATH && export PATH &&
> mkdir someproject && cd someproject &&
> git aliasedinit
> fatal: cannot exec 'git-aliasedinit': Permission denied
Imagine you did not have alias.aliasedinit in ~/.gitconfig but had a
script called $(pwd)/searchpath/git-aliasedinit which we would fail to
execute. What message would we get in that case? Currently I think we get
permission denied.
Would we get the same with your patch, or something that does not hint
at all that there is a permission problem?
See also the "tangent" part of
http://thread.gmane.org/gmane.comp.version-control.git/171755
and the discussion that follows it. I do not think we reached any
conclusion nor a patch.
^ permalink raw reply
* Re: [RFC] deprecating and eventually removing "git relink"?
From: Phillip Susi @ 2011-11-22 1:58 UTC (permalink / raw)
To: Jeff King; +Cc: Simon Brenner, git
In-Reply-To: <20111121221934.GA21882@sigill.intra.peff.net>
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
On 11/21/2011 05:19 PM, Jeff King wrote:
> Yes, that is one way to do it. The big drawback there is that by
> using hard links, you can only share objects between repos within
> the same filesystem.
Yep, hard links requires same filesystem, but means you don't have to
have a central repo that you have to gc very carefully.
> So yeah, I think it's a perfectly reasonable approach, if you don't
> mind the hard link requirement, and your relink is something like
> "git relink ~/linux-repos/*".
That's the idea.
To sum up, it appears there are 3 possible implementations:
1) hard link + master repo with mutual awareness
2) hard link + no master repo or inter-repo awareness
3) alternatives + master repo with mutual awareness
With #1 you can auto relink when any child does gc
With #2 the repos don't need to be aware of each other
With #3 the repos don't need to be on the same fs, can auto relink
when any child does gc, but moving a child or removing the master repo
causes breakage
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.11 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/
iEYEARECAAYFAk7LAcgACgkQJ4UciIs+XuKzrQCeIb2Tb3D+nqDlF5bBD8vkQy/t
4sQAniEbL2kZK2wvY+y4tvd+QDRh1G85
=QHQ5
-----END PGP SIGNATURE-----
^ permalink raw reply
* Re: [PATCH] convert.c: Fix return type of git_path_check_eol()
From: Ramkumar Ramachandra @ 2011-11-22 2:03 UTC (permalink / raw)
To: Ramsay Jones; +Cc: Junio C Hamano, GIT Mailing-list
In-Reply-To: <4ECA9B81.9070101@ramsay1.demon.co.uk>
Hi Ramsay,
Ramsay Jones wrote:
> [...]
> diff --git a/convert.c b/convert.c
> index 038b0be..86e9c29 100644
> --- a/convert.c
> +++ b/convert.c
> @@ -658,7 +658,7 @@ static enum crlf_action git_path_check_crlf(const char *path, struct git_attr_ch
> return CRLF_GUESS;
> }
>
> -static enum crlf_action git_path_check_eol(const char *path, struct git_attr_check *check)
> +static enum eol git_path_check_eol(const char *path, struct git_attr_check *check)
> {
> const char *value = check->value;
>
Looks good. Thanks.
-- Ram
^ permalink raw reply
* Re: Possible bug with branch names and case sensitivity
From: Michael Haggerty @ 2011-11-22 5:21 UTC (permalink / raw)
To: Jay Soffian; +Cc: Gerd Knops, git
In-Reply-To: <CAG+J_Dz6nK5fPhBRmoojmgYSv5OviN7pfgNKnRy9_9WmDS1_2w@mail.gmail.com>
On 11/21/2011 08:18 PM, Jay Soffian wrote:
> On Sat, Nov 19, 2011 at 3:08 PM, Gerd Knops <gerti@bitart.com>
> wrote:
>> On Mac OS X with a case-insensitive file system (not sure if that
>> matters) git get's confused with branch names that differ only in
>> case.
>
> This is true. The branch code assumes a case-sensitive filesystem. I
> started working on a fix, but it was more involved than I first
> thought it would be. See my local WIP commit below, apologies if
> gmail lines wraps it.
Is it obvious how references *should* be handled on case-insensitive
filesystems? It's certainly not obvious to me (has it been discussed
elsewhere?) I don't think it is a good idea to "fix" this one problem
without defining an overall policy.
Currently git handles references names case-sensitively and allows
multiple reference names that differ only in case. If this behavior is
to be preserved on case-insensitive filesystems, then either loose
references must be stored differently (e.g., multiple references in the
same file) or ambiguous references need always to be packed. Moreover,
given a refname, we would need to be careful not to just try to open a
file with that name and assume that it is the correct reference; rather,
we would have to ask the filesystem for the name of the file in its
original case and make sure that it agrees with the case of the refname
that we seek.
By the way, this could have ramifications for the recently-added test
that top-level refnames should be in ALL_CAPS.
If we want to consider bending git's behavior, there are a number of
ways we could go:
1. Remain case-sensitive but prohibit refnames that differ only in case.
2. Remain case-sensitive but prohibit refnames that differ only in case
*when running on a case-insensitive filesystem*.
3. Change the handling of refnames to be case-insensitive but
case-preserving.
The above all assumes a case-insensitive filesystem that is
*case-preserving*. If we want to support filesystems that do not
preserve case, things get even more complicated.
And if we want to pretend to support non-ASCII refnames, then the issue
of encodings is another nasty can of worms...
Michael
--
Michael Haggerty
mhagger@alum.mit.edu
http://softwareswirl.blogspot.com/
^ permalink raw reply
* Re: [PATCH] Documentation update for 'git branch --list'
From: Vincent van Ravesteijn @ 2011-11-22 6:40 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git, git
In-Reply-To: <7v7h2tgyw6.fsf@alter.siamese.dyndns.org>
Op 21-11-2011 18:37, Junio C Hamano schreef:
> Vincent van Ravesteijn<vfr@lyx.org> writes:
>
>> Indicate that the<pattern>... argument is only valid when using --list.
>> Otherwise a branch with the name<pattern> will be created.
> I actually think there are some bugs in the implementation along that line
> and the way forward is to fix the implementation, instead of documenting
> the buggy behaviour.
>
> It is natural to expect "git branch --merged pu vr/\*" to list branches
> that are contained in 'pu' whose names match the given pattern, but it
> seems to try creating a branch called "vr/*" and fails, for example.
If this is what you naturally would expect, I would expect the following
"git branch vr/*" to work as well.
What would you say if we try to interpret the argument as a pattern when
the argument is not a valid ref name ? This includes all arguments
containing '*'. In case of confusion we can demand the "--list"
parameter to make clear what we mean.
Another thing is noticed is that the pattern use is different for 'git
for-each-ref <pattern...>' and 'git branch --list <pattern...>'.
Two examples:
> c:\Users\Vincent\Documents\git\git>git for-each-ref refs/heads/
> f56ef114eeefee755f422e6cbef2d83974cb90f1 commit refs/heads/master
> c2ee0c73bcb4d1980e5d0bb0d20912b565d8ae38 commit refs/heads/next
> e341d009b2e0374520ac72973c6e98d315218624 commit refs/heads/pu
> 5fd37a7da3df644a85887051ae36117c732d1781 commit refs/heads/vr/fix-crashes
> cec815158934d82cdef815ea1342be2c3edbc5c8 commit
> refs/heads/vr/msvc-compile-fix
> 777963bd4a9f41ac3eae8de2a7b7177917d127e6 commit
> refs/heads/vr/msvc-compile-fix-poll
>
> c:\Users\Vincent\Documents\git\git>git branch --list refs/heads/
>
> c:\Users\Vincent\Documents\git\git>
and
> c:\Users\Vincent\Documents\git\git>git branch --list v*
> vr/fix-crashes
> vr/msvc-compile-fix
> vr/msvc-compile-fix-poll
>
> c:\Users\Vincent\Documents\git\git>git for-each-ref v*
>
> c:\Users\Vincent\Documents\git\git>
Vincent
^ permalink raw reply
* Re: [msysGit] [PATCH 1/2] MSVC: Do not close stdout to prevent a crash
From: Vincent van Ravesteijn @ 2011-11-22 6:45 UTC (permalink / raw)
To: Johannes Sixt; +Cc: git, msysgit, gitster, kusmabite, Johannes.Schindelin
In-Reply-To: <4EC80D84.2010202@kdbg.org>
>> To prevent the crash and to prevent git from writing cruft into the patch
>> file, we do not close stdout, but redirect it to "nul" instead.
> A more robust solution is to add invalidcontinue.obj to the linker
> command line. This installs an invalid argument handler that does not
> abort, and restores a sane behavior.
This seems to work only for release builds or did I miss something ?
Is there any argument to not redirect stdout to "/dev/null" on all
platforms ?
Vincent
^ permalink raw reply
* Re: [PATCH 2/3] Compile fix for MSVC: fix poll-related macro redefines
From: Vincent van Ravesteijn @ 2011-11-22 7:30 UTC (permalink / raw)
To: Sebastian Schuberth
Cc: git, gitster, kusmabite, msysgit, j.sixt, Johannes Schindelin
In-Reply-To: <4EC6978D.3020604@gmail.com>
Op 18-11-2011 18:36, Sebastian Schuberth schreef:
> On 18.11.2011 17:44, Vincent van Ravesteijn wrote:
>
>> diff --git a/git-compat-util.h b/git-compat-util.h
>> index 5ef8ff7..76cbfe6 100644
>> --- a/git-compat-util.h
>> +++ b/git-compat-util.h
>> @@ -85,6 +85,7 @@
>> #define _SGI_SOURCE 1
>>
>> #ifdef WIN32 /* Both MinGW and MSVC */
>> +# define _WIN32_WINNT 0x0501
>> #define WIN32_LEAN_AND_MEAN /* stops windows.h including winsock.h */
>> #include<winsock2.h>
>> #include<windows.h>
>
> It seems the indentation is wrong (does not match the surrounding
> code) here.
>
Yes, you're right. I will reroll the patch series when there is somewhat
more feedback on the series.
Vincent
^ permalink raw reply
* Re: [PATCH] run-command.c: Accept EACCES as command not found
From: Frans Klaver @ 2011-11-22 9:31 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
In-Reply-To: <7v62idf2vy.fsf@alter.siamese.dyndns.org>
On Tue, Nov 22, 2011 at 12:54 AM, Junio C Hamano <gitster@pobox.com> wrote:
> Imagine you did not have alias.aliasedinit in ~/.gitconfig but had a
> script called $(pwd)/searchpath/git-aliasedinit which we would fail to
> execute. What message would we get in that case? Currently I think we get
> permission denied.
Correct.
> Would we get the same with your patch, or something that does not hint
> at all that there is a permission problem?
Nope. That would be just as confusing and inarguably incorrect at that
-- bash differentiates between commands that exist, but cannot be
executed due to permissions (access denied) and paths that cannot be
read (they are ignored in the search).
> See also the "tangent" part of
>
> http://thread.gmane.org/gmane.comp.version-control.git/171755
>
> and the discussion that follows it. I do not think we reached any
> conclusion nor a patch.
There's no black-on-white conclusion there. I get the impression that
no one really has an idea of what they want when encountering EACCES.
Git has to do what's reasonable to provide the user with information.
Currently I think it does too little. Jonathan N. gave the option of
optionally using libexplain[1]. It's pretty verbose and accurate:
fatal: cannot exec 'git-frotz': execvp(pathname = "git-frotz", argv =
["git-frotz"]) failed, Permission denied (13, EACCES) because the
process does not have search permission to the pathname
"/home/frans/devsw/searchpath" directory, the process effective UID
1000 "frans" matches the directory owner UID 1000 "frans" and the
owner permission mode is "r--", and the process is not privileged
(does not have the DAC_READ_SEARCH capability): Success
I wouldn't be in favor of adding the dependency just to enable users
to track down PATH issues though. Also, I think "Cannot access
/home/frans/devsw/searchpath" would just as well do the trick.
For Jonathan's example[2] libexplain doesn't have a clear answer either:
fatal: cannot exec 'git-frotz': execvp(pathname = "git-frotz", argv =
["git-frotz"]) failed, Permission denied (13, EACCES): Permission
denied
If git is going to do some diagnostics on why the execvp returned
EACCES, it can still give a few hints. Most of the more likely options
are then ruled out.
Frans
[1] http://article.gmane.org/gmane.comp.version-control.git/171860
[2] http://article.gmane.org/gmane.comp.version-control.git/171848
^ permalink raw reply
* Re: Feature requset: listing of current stash in git gui
From: David Aguilar @ 2011-11-22 10:24 UTC (permalink / raw)
To: dexen deVries; +Cc: git@vger.kernel.org
In-Reply-To: <30417194.1411.1321907485839.JavaMail.mobile-sync@ieja14>
On Nov 21, 2011, at 4:41 AM, dexen deVries <dexen.devries@gmail.com> wrote:
> On Monday 21 of November 2011 11:53:03 Stefan Näwe wrote:
>> You can simply put the following in your ~/.gitconfig:
>>
>> [guitool "Stash/show"]
>> cmd = git stash show -p
>> [guitool "Stash/list"]
>> cmd = git stash list
>> [guitool "Stash/pop"]
>> cmd = git stash pop
>> [guitool "Stash/drop"]
>> cmd = git stash drop
>> confirm = yes
>
> that's nice, but doesn't list visually the stashed changes. I'd rather have a
> listing similar to Unstanged Changes / Staged Changes, with ability to view
> line-by-line diff just like for Changes.
[blatant plug]
git-cola has this feature.
>
http://git-cola.github.com/
Have fun,
--
David
^ permalink raw reply
* [PATCH v2 0/3] Re: cherry-pick/revert error messages
From: Jonathan Nieder @ 2011-11-22 11:12 UTC (permalink / raw)
To: Ramkumar Ramachandra
Cc: git, Christian Couder, Martin von Zweigbergk, Phil Hord,
Jay Soffian
In-Reply-To: <20111120094650.GB2278@elie.hsd1.il.comcast.net>
Hi again,
Jonathan Nieder wrote:
> Ramkumar Ramachandra wrote:
>> Looks much better! Yes, a series pretty'fying error messages would be
>> really nice.
>
> Good to hear. Here's a rough one.
Here's a less rough version. Same scope.
Patch 1 renames --reset to --quit, keeping --reset as a synonym for
backward compatibility this time. I don't suspect anyone was using
--reset, but splitting out the backward-incompatible changes seems
like a useful habit to follow nonetheless.
Patch 2 is a small code cleanup in preparation for patch 3, unchanged
from v1.
The change description for patch 3 said 'double redundant first
"error:" message', which was kind of amusing. This time it says
'redundant first "error:" message'.
More notably, this version has some bonus patches:
Patch 4 introduces the REVERT_HEAD pseudoref, which is analagous to
CHERRY_PICK_HEAD except it is populated by "git revert" and does not
influence the behavior of "git commit". This could allow tools like
__git_ps1 to detect when we are in the middle of a conflicted revert,
though the series does not include a patch to do that. I'm kind of
fond of it.
Patch 5 introduces a "git cherry-pick --abort" command that rolls back
the series of commits made so far in a conflicted cherry-pick.
Compare --quit, which tells cherry-pick to leave HEAD alone and get
out of the way.
Patch 6 removes the --reset compatibility option. There might not be
much reason to do that, aside from removing clutter and saving a few
bytes. The alternate motivation of removing the option to prevent
people from relying on it seems less compelling after patch 1 made it
harder to stumble upon the option.
May the series can provide some amusement.
Thanks to Junio and Jakub for the useful feedback so far.
Jonathan Nieder (3):
revert: rename --reset option to --quit
revert: rearrange pick_revisions() for clarity
revert: improve error message for cherry-pick during cherry-pick
Documentation/git-cherry-pick.txt | 2 +-
Documentation/git-revert.txt | 2 +-
Documentation/sequencer.txt | 10 ++--
builtin/revert.c | 74 +++++++++++++++++++------------------
sequencer.h | 2 +-
t/t3510-cherry-pick-sequence.sh | 31 +++++++++++++--
t/t7106-reset-sequence.sh | 2 +-
7 files changed, 73 insertions(+), 50 deletions(-)
^ permalink raw reply
* [PATCH 1/3] revert: rename --reset option to --quit
From: Jonathan Nieder @ 2011-11-22 11:14 UTC (permalink / raw)
To: Ramkumar Ramachandra
Cc: git, Christian Couder, Martin von Zweigbergk, Phil Hord,
Jay Soffian
In-Reply-To: <20111122111207.GA7399@elie.hsd1.il.comcast.net>
The option to "git cherry-pick" and "git revert" to discard the
sequencer state introduced by v1.7.8-rc0~141^2~6 (revert: Introduce
--reset to remove sequencer state, 2011-08-04) has a confusing name.
Change it now, while we still have the time.
The new name for "cherry-pick, please get out of my way, since I've
long forgotten about the sequence of commits I was cherry-picking when
you wrote that old .git/sequencer directory" is --quit. Mnemonic:
this is analagous to quiting a program the user is no longer using ---
we just want to get out of the multiple-command cherry-pick procedure
and not to reset HEAD or rewind any other old state.
The "--reset" option is kept as a synonym to minimize the impact. We
might consider dropping it for simplicity in a separate patch, though.
Adjust documentation and tests to use the newly preferred name (--quit)
instead of --reset. While at it, let's clarify the short descriptions
of these operations in "-h" output.
Before:
--reset forget the current operation
--continue continue the current operation
After:
--quit end revert or cherry-pick sequence
--continue resume revert or cherry-pick sequence
Noticed-by: Phil Hord <phil.hord@gmail.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
Documentation/git-cherry-pick.txt | 2 +-
Documentation/git-revert.txt | 2 +-
Documentation/sequencer.txt | 10 +++++-----
builtin/revert.c | 25 ++++++++++++++-----------
sequencer.h | 2 +-
t/t3510-cherry-pick-sequence.sh | 31 ++++++++++++++++++++++++++-----
t/t7106-reset-sequence.sh | 2 +-
7 files changed, 49 insertions(+), 25 deletions(-)
diff --git a/Documentation/git-cherry-pick.txt b/Documentation/git-cherry-pick.txt
index 2660a842..21998b82 100644
--- a/Documentation/git-cherry-pick.txt
+++ b/Documentation/git-cherry-pick.txt
@@ -9,8 +9,8 @@ SYNOPSIS
--------
[verse]
'git cherry-pick' [--edit] [-n] [-m parent-number] [-s] [-x] [--ff] <commit>...
-'git cherry-pick' --reset
'git cherry-pick' --continue
+'git cherry-pick' --quit
DESCRIPTION
-----------
diff --git a/Documentation/git-revert.txt b/Documentation/git-revert.txt
index f3519413..b0fcabc8 100644
--- a/Documentation/git-revert.txt
+++ b/Documentation/git-revert.txt
@@ -9,8 +9,8 @@ SYNOPSIS
--------
[verse]
'git revert' [--edit | --no-edit] [-n] [-m parent-number] [-s] <commit>...
-'git revert' --reset
'git revert' --continue
+'git revert' --quit
DESCRIPTION
-----------
diff --git a/Documentation/sequencer.txt b/Documentation/sequencer.txt
index 3e6df338..75f8e869 100644
--- a/Documentation/sequencer.txt
+++ b/Documentation/sequencer.txt
@@ -1,9 +1,9 @@
---reset::
- Forget about the current operation in progress. Can be used
- to clear the sequencer state after a failed cherry-pick or
- revert.
-
--continue::
Continue the operation in progress using the information in
'.git/sequencer'. Can be used to continue after resolving
conflicts in a failed cherry-pick or revert.
+
+--quit::
+ Forget about the current operation in progress. Can be used
+ to clear the sequencer state after a failed cherry-pick or
+ revert.
diff --git a/builtin/revert.c b/builtin/revert.c
index 544e8c30..e109fb11 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -40,7 +40,7 @@ static const char * const cherry_pick_usage[] = {
};
enum replay_action { REVERT, CHERRY_PICK };
-enum replay_subcommand { REPLAY_NONE, REPLAY_RESET, REPLAY_CONTINUE };
+enum replay_subcommand { REPLAY_NONE, REPLAY_REMOVE_STATE, REPLAY_CONTINUE };
struct replay_opts {
enum replay_action action;
@@ -133,11 +133,11 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)
{
const char * const * usage_str = revert_or_cherry_pick_usage(opts);
const char *me = action_name(opts);
- int reset = 0;
+ int remove_state = 0;
int contin = 0;
struct option options[] = {
- OPT_BOOLEAN(0, "reset", &reset, "forget the current operation"),
- OPT_BOOLEAN(0, "continue", &contin, "continue the current operation"),
+ OPT_BOOLEAN(0, "quit", &remove_state, "end revert or cherry-pick sequence"),
+ OPT_BOOLEAN(0, "continue", &contin, "resume revert or cherry-pick sequence"),
OPT_BOOLEAN('n', "no-commit", &opts->no_commit, "don't automatically commit"),
OPT_BOOLEAN('e', "edit", &opts->edit, "edit the commit message"),
OPT_NOOP_NOARG('r', NULL),
@@ -147,6 +147,9 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)
OPT_STRING(0, "strategy", &opts->strategy, "strategy", "merge strategy"),
OPT_CALLBACK('X', "strategy-option", &opts, "option",
"option for merge strategy", option_parse_x),
+ { OPTION_BOOLEAN, 0, "reset", &remove_state, NULL,
+ "alias for --quit (deprecated)",
+ PARSE_OPT_HIDDEN | PARSE_OPT_NOARG },
OPT_END(),
OPT_END(),
OPT_END(),
@@ -168,13 +171,13 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)
/* Check for incompatible subcommands */
verify_opt_mutually_compatible(me,
- "--reset", reset,
+ "--quit", remove_state,
"--continue", contin,
NULL);
/* Set the subcommand */
- if (reset)
- opts->subcommand = REPLAY_RESET;
+ if (remove_state)
+ opts->subcommand = REPLAY_REMOVE_STATE;
else if (contin)
opts->subcommand = REPLAY_CONTINUE;
else
@@ -183,8 +186,8 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)
/* Check for incompatible command line arguments */
if (opts->subcommand != REPLAY_NONE) {
char *this_operation;
- if (opts->subcommand == REPLAY_RESET)
- this_operation = "--reset";
+ if (opts->subcommand == REPLAY_REMOVE_STATE)
+ this_operation = "--quit";
else
this_operation = "--continue";
@@ -965,7 +968,7 @@ static int pick_revisions(struct replay_opts *opts)
* cherry-pick should be handled differently from an existing
* one that is being continued
*/
- if (opts->subcommand == REPLAY_RESET) {
+ if (opts->subcommand == REPLAY_REMOVE_STATE) {
remove_sequencer_state(1);
return 0;
} else if (opts->subcommand == REPLAY_CONTINUE) {
@@ -988,7 +991,7 @@ static int pick_revisions(struct replay_opts *opts)
if (create_seq_dir() < 0) {
error(_("A cherry-pick or revert is in progress."));
advise(_("Use --continue to continue the operation"));
- advise(_("or --reset to forget about it"));
+ advise(_("or --quit to forget about it"));
return -1;
}
if (get_sha1("HEAD", sha1)) {
diff --git a/sequencer.h b/sequencer.h
index 905d2950..f435fdb4 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -13,7 +13,7 @@
*
* With the aggressive flag, it additionally removes SEQ_OLD_DIR,
* ignoring any errors. Inteded to be used by the sequencer's
- * '--reset' subcommand.
+ * '--quit' subcommand.
*/
void remove_sequencer_state(int aggressive);
diff --git a/t/t3510-cherry-pick-sequence.sh b/t/t3510-cherry-pick-sequence.sh
index 3bca2b3d..bb67cdcb 100755
--- a/t/t3510-cherry-pick-sequence.sh
+++ b/t/t3510-cherry-pick-sequence.sh
@@ -13,7 +13,7 @@ test_description='Test cherry-pick continuation features
. ./test-lib.sh
pristine_detach () {
- git cherry-pick --reset &&
+ git cherry-pick --quit &&
git checkout -f "$1^0" &&
git read-tree -u --reset HEAD &&
git clean -d -f -f -q -x
@@ -70,18 +70,39 @@ test_expect_success 'cherry-pick cleans up sequencer state upon success' '
test_path_is_missing .git/sequencer
'
-test_expect_success '--reset does not complain when no cherry-pick is in progress' '
+test_expect_success '--quit does not complain when no cherry-pick is in progress' '
pristine_detach initial &&
- git cherry-pick --reset
+ git cherry-pick --quit
'
-test_expect_success '--reset cleans up sequencer state' '
+test_expect_success '--quit cleans up sequencer state' '
pristine_detach initial &&
test_must_fail git cherry-pick base..picked &&
- git cherry-pick --reset &&
+ git cherry-pick --quit &&
test_path_is_missing .git/sequencer
'
+test_expect_success 'cherry-pick --reset (another name for --quit)' '
+ pristine_detach initial &&
+ cat >expect <<-\EOF &&
+ OBJID
+ :100644 100644 OBJID OBJID M unrelated
+ OBJID
+ :000000 100644 OBJID OBJID A foo
+ :000000 100644 OBJID OBJID A unrelated
+ EOF
+ test_must_fail git cherry-pick base..picked &&
+ git cherry-pick --reset &&
+ test_path_is_missing .git/sequencer &&
+ test_must_fail git update-index --refresh &&
+ {
+ git rev-list HEAD |
+ git diff-tree --root --stdin |
+ sed "s/$_x40/OBJID/g"
+ } >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'cherry-pick cleans up sequencer state when one commit is left' '
pristine_detach initial &&
test_must_fail git cherry-pick base..picked &&
diff --git a/t/t7106-reset-sequence.sh b/t/t7106-reset-sequence.sh
index 4956caaf..3f86e8c5 100755
--- a/t/t7106-reset-sequence.sh
+++ b/t/t7106-reset-sequence.sh
@@ -12,7 +12,7 @@ test_description='Test interaction of reset --hard with sequencer
. ./test-lib.sh
pristine_detach () {
- git cherry-pick --reset &&
+ git cherry-pick --quit &&
git checkout -f "$1^0" &&
git read-tree -u --reset HEAD &&
git clean -d -f -f -q -x
--
1.7.8.rc3
^ permalink raw reply related
* [PATCH 2/3] revert: rearrange pick_revisions() for clarity
From: Jonathan Nieder @ 2011-11-22 11:15 UTC (permalink / raw)
To: Ramkumar Ramachandra
Cc: git, Christian Couder, Martin von Zweigbergk, Phil Hord,
Jay Soffian
In-Reply-To: <20111122111207.GA7399@elie.hsd1.il.comcast.net>
Deal completely with "cherry-pick --quit" and --continue at the
beginning of pick_revisions(), leaving the rest of the function for
the more interesting "git cherry-pick <commits>" case.
No functional change intended. The impact is just to unindent the
code a little.
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
builtin/revert.c | 48 ++++++++++++++++++++++++------------------------
1 files changed, 24 insertions(+), 24 deletions(-)
diff --git a/builtin/revert.c b/builtin/revert.c
index e109fb11..2346cafb 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -971,40 +971,40 @@ static int pick_revisions(struct replay_opts *opts)
if (opts->subcommand == REPLAY_REMOVE_STATE) {
remove_sequencer_state(1);
return 0;
- } else if (opts->subcommand == REPLAY_CONTINUE) {
+ }
+ if (opts->subcommand == REPLAY_CONTINUE) {
if (!file_exists(git_path(SEQ_TODO_FILE)))
- goto error;
+ return error(_("No %s in progress"), action_name(opts));
read_populate_opts(&opts);
read_populate_todo(&todo_list, opts);
/* Verify that the conflict has been resolved */
if (!index_differs_from("HEAD", 0))
todo_list = todo_list->next;
- } else {
- /*
- * Start a new cherry-pick/ revert sequence; but
- * first, make sure that an existing one isn't in
- * progress
- */
+ return pick_commits(todo_list, opts);
+ }
+
+ /*
+ * Start a new cherry-pick/ revert sequence; but
+ * first, make sure that an existing one isn't in
+ * progress
+ */
- walk_revs_populate_todo(&todo_list, opts);
- if (create_seq_dir() < 0) {
- error(_("A cherry-pick or revert is in progress."));
- advise(_("Use --continue to continue the operation"));
- advise(_("or --quit to forget about it"));
- return -1;
- }
- if (get_sha1("HEAD", sha1)) {
- if (opts->action == REVERT)
- return error(_("Can't revert as initial commit"));
- return error(_("Can't cherry-pick into empty head"));
- }
- save_head(sha1_to_hex(sha1));
- save_opts(opts);
+ walk_revs_populate_todo(&todo_list, opts);
+ if (create_seq_dir() < 0) {
+ error(_("A cherry-pick or revert is in progress."));
+ advise(_("Use --continue to continue the operation"));
+ advise(_("or --quit to forget about it"));
+ return -1;
+ }
+ if (get_sha1("HEAD", sha1)) {
+ if (opts->action == REVERT)
+ return error(_("Can't revert as initial commit"));
+ return error(_("Can't cherry-pick into empty head"));
}
+ save_head(sha1_to_hex(sha1));
+ save_opts(opts);
return pick_commits(todo_list, opts);
-error:
- return error(_("No %s in progress"), action_name(opts));
}
int cmd_revert(int argc, const char **argv, const char *prefix)
--
1.7.8.rc3
^ permalink raw reply related
* [PATCH 3/3] revert: improve error message for cherry-pick during cherry-pick
From: Jonathan Nieder @ 2011-11-22 11:15 UTC (permalink / raw)
To: Ramkumar Ramachandra
Cc: git, Christian Couder, Martin von Zweigbergk, Phil Hord,
Jay Soffian
In-Reply-To: <20111122111207.GA7399@elie.hsd1.il.comcast.net>
In the spirit of v1.6.3.3~3^2 (refuse to merge during a merge,
2009-07-01), "git cherry-pick" refuses to start a new cherry-pick when
in the middle of an existing conflicted cherry-pick in the following
sequence:
1. git cherry-pick HEAD..origin
2. resolve conflicts
3. git cherry-pick HEAD..origin (instead of "git cherry-pick
--continue", by mistake)
Good. However, the error message on attempting step 3 is more
convoluted than necessary:
$ git cherry-pick HEAD..origin
error: .git/sequencer already exists.
error: A cherry-pick or revert is in progress.
hint: Use --continue to continue the operation
hint: or --quit to forget about it
fatal: cherry-pick failed
Clarify by removing the redundant first "error:" message, simplifying
the advice, and using lower-case and no full stops to be consistent
with other commands that prefix their messages with "error:", so it
becomes
error: a cherry-pick or revert is already in progress
hint: try "git cherry-pick (--continue | --quit)"
fatal: cherry-pick failed
The "fatal: cherry-pick failed" line seems unnecessary, too, but
that can be fixed some other day.
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
builtin/revert.c | 13 ++++++-------
1 files changed, 6 insertions(+), 7 deletions(-)
diff --git a/builtin/revert.c b/builtin/revert.c
index 2346cafb..1d112e4c 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -846,8 +846,11 @@ static int create_seq_dir(void)
{
const char *seq_dir = git_path(SEQ_DIR);
- if (file_exists(seq_dir))
- return error(_("%s already exists."), seq_dir);
+ if (file_exists(seq_dir)) {
+ error(_("a cherry-pick or revert is already in progress"));
+ advise(_("try \"git cherry-pick (--continue | --quit)\""));
+ return -1;
+ }
else if (mkdir(seq_dir, 0777) < 0)
die_errno(_("Could not create sequencer directory %s"), seq_dir);
return 0;
@@ -991,12 +994,8 @@ static int pick_revisions(struct replay_opts *opts)
*/
walk_revs_populate_todo(&todo_list, opts);
- if (create_seq_dir() < 0) {
- error(_("A cherry-pick or revert is in progress."));
- advise(_("Use --continue to continue the operation"));
- advise(_("or --quit to forget about it"));
+ if (create_seq_dir() < 0)
return -1;
- }
if (get_sha1("HEAD", sha1)) {
if (opts->action == REVERT)
return error(_("Can't revert as initial commit"));
--
1.7.8.rc3
^ permalink raw reply related
* [PATCH 4/3] revert: write REVERT_HEAD pseudoref during conflicted revert
From: Jonathan Nieder @ 2011-11-22 11:17 UTC (permalink / raw)
To: Ramkumar Ramachandra
Cc: git, Christian Couder, Martin von Zweigbergk, Phil Hord,
Jay Soffian
In-Reply-To: <20111122111207.GA7399@elie.hsd1.il.comcast.net>
When conflicts are encountered while reverting a commit, it can be
handy to have the name of that commit easily available. For example,
to produce a copy of the patch to refer to while resolving conflicts:
$ git revert 2eceb2a8
error: could not revert 2eceb2a8... awesome, buggy feature
$ git show -R REVERT_HEAD >the-patch
$ edit $(git diff --name-only)
Set a REVERT_HEAD pseudoref when "git revert" does not make a commit,
for cases like this. This also makes it possible for scripts to
distinguish between a revert that encountered conflicts and other
sources of an unmerged index.
After successfully committing, resetting with "git reset", or moving
to another commit with "git checkout" or "git reset", the pseudoref is
no longer useful, so remove it.
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
branch.c | 1 +
builtin/commit.c | 1 +
builtin/revert.c | 8 +++--
t/t3507-cherry-pick-conflict.sh | 54 +++++++++++++++++++++++++++++++++++++++
4 files changed, 61 insertions(+), 3 deletions(-)
diff --git a/branch.c b/branch.c
index d8098762..025a97be 100644
--- a/branch.c
+++ b/branch.c
@@ -241,6 +241,7 @@ void create_branch(const char *head,
void remove_branch_state(void)
{
unlink(git_path("CHERRY_PICK_HEAD"));
+ unlink(git_path("REVERT_HEAD"));
unlink(git_path("MERGE_HEAD"));
unlink(git_path("MERGE_RR"));
unlink(git_path("MERGE_MSG"));
diff --git a/builtin/commit.c b/builtin/commit.c
index c46f2d18..8f2bebec 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -1514,6 +1514,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
}
unlink(git_path("CHERRY_PICK_HEAD"));
+ unlink(git_path("REVERT_HEAD"));
unlink(git_path("MERGE_HEAD"));
unlink(git_path("MERGE_MSG"));
unlink(git_path("MERGE_MODE"));
diff --git a/builtin/revert.c b/builtin/revert.c
index 1d112e4c..f5ba67a5 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -289,7 +289,7 @@ static char *get_encoding(const char *message)
return NULL;
}
-static void write_cherry_pick_head(struct commit *commit)
+static void write_cherry_pick_head(struct commit *commit, const char *pseudoref)
{
const char *filename;
int fd;
@@ -297,7 +297,7 @@ static void write_cherry_pick_head(struct commit *commit)
strbuf_addf(&buf, "%s\n", sha1_to_hex(commit->object.sha1));
- filename = git_path("CHERRY_PICK_HEAD");
+ filename = git_path(pseudoref);
fd = open(filename, O_WRONLY | O_CREAT, 0666);
if (fd < 0)
die_errno(_("Could not open '%s' for writing"), filename);
@@ -597,7 +597,9 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)
* write it at all.
*/
if (opts->action == CHERRY_PICK && !opts->no_commit && (res == 0 || res == 1))
- write_cherry_pick_head(commit);
+ write_cherry_pick_head(commit, "CHERRY_PICK_HEAD");
+ if (opts->action == REVERT && ((opts->no_commit && res == 0) || res == 1))
+ write_cherry_pick_head(commit, "REVERT_HEAD");
if (res) {
error(opts->action == REVERT
diff --git a/t/t3507-cherry-pick-conflict.sh b/t/t3507-cherry-pick-conflict.sh
index cb45574a..ee1659c1 100755
--- a/t/t3507-cherry-pick-conflict.sh
+++ b/t/t3507-cherry-pick-conflict.sh
@@ -253,6 +253,60 @@ test_expect_success 'revert also handles conflicts sanely' '
test_cmp expected actual
'
+test_expect_success 'failed revert sets REVERT_HEAD' '
+ pristine_detach initial &&
+ test_must_fail git revert picked &&
+ test_cmp_rev picked REVERT_HEAD
+'
+
+test_expect_success 'successful revert does not set REVERT_HEAD' '
+ pristine_detach base &&
+ git revert base &&
+ test_must_fail git rev-parse --verify CHERRY_PICK_HEAD &&
+ test_must_fail git rev-parse --verify REVERT_HEAD
+'
+
+test_expect_success 'revert --no-commit sets REVERT_HEAD' '
+ pristine_detach base &&
+ git revert --no-commit base &&
+ test_must_fail git rev-parse --verify CHERRY_PICK_HEAD &&
+ test_cmp_rev base REVERT_HEAD
+'
+
+test_expect_success 'revert w/dirty tree does not set REVERT_HEAD' '
+ pristine_detach base &&
+ echo foo > foo &&
+ test_must_fail git revert base &&
+ test_must_fail git rev-parse --verify CHERRY_PICK_HEAD &&
+ test_must_fail git rev-parse --verify REVERT_HEAD
+'
+
+test_expect_success 'GIT_CHERRY_PICK_HELP does not suppress REVERT_HEAD' '
+ pristine_detach initial &&
+ (
+ GIT_CHERRY_PICK_HELP="and then do something else" &&
+ GIT_REVERT_HELP="and then do something else, again" &&
+ export GIT_CHERRY_PICK_HELP GIT_REVERT_HELP &&
+ test_must_fail git revert picked
+ ) &&
+ test_must_fail git rev-parse --verify CHERRY_PICK_HEAD &&
+ test_cmp_rev picked REVERT_HEAD
+'
+
+test_expect_success 'git reset clears REVERT_HEAD' '
+ pristine_detach initial &&
+ test_must_fail git revert picked &&
+ git reset &&
+ test_must_fail git rev-parse --verify REVERT_HEAD
+'
+
+test_expect_success 'failed commit does not clear REVERT_HEAD' '
+ pristine_detach initial &&
+ test_must_fail git revert picked &&
+ test_must_fail git commit &&
+ test_cmp_rev picked REVERT_HEAD
+'
+
test_expect_success 'revert conflict, diff3 -m style' '
pristine_detach initial &&
git config merge.conflictstyle diff3 &&
--
1.7.8.rc3
^ permalink raw reply related
* [PATCH 5/3] revert: introduce --abort to cancel a failed cherry-pick
From: Jonathan Nieder @ 2011-11-22 11:20 UTC (permalink / raw)
To: Ramkumar Ramachandra
Cc: git, Christian Couder, Martin von Zweigbergk, Phil Hord,
Jay Soffian
In-Reply-To: <20111122111207.GA7399@elie.hsd1.il.comcast.net>
After running some ill-advised command like "git cherry-pick
HEAD..linux-next", the bewildered novice may want to return to more
familiar territory. Introduce a "git cherry-pick --abort" command
that rolls back the entire cherry-pick sequence and places the
repository back on solid ground.
Just like "git merge --abort", this internally uses "git reset
--merge", so local changes not involved in the conflict resolution are
preserved.
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
Documentation/git-cherry-pick.txt | 1 +
Documentation/git-revert.txt | 1 +
Documentation/sequencer.txt | 3 +
builtin/revert.c | 87 ++++++++++++++++++++++++++++++-
t/t3510-cherry-pick-sequence.sh | 102 ++++++++++++++++++++++++++++++++++++-
5 files changed, 189 insertions(+), 5 deletions(-)
diff --git a/Documentation/git-cherry-pick.txt b/Documentation/git-cherry-pick.txt
index 21998b82..fed5097e 100644
--- a/Documentation/git-cherry-pick.txt
+++ b/Documentation/git-cherry-pick.txt
@@ -11,6 +11,7 @@ SYNOPSIS
'git cherry-pick' [--edit] [-n] [-m parent-number] [-s] [-x] [--ff] <commit>...
'git cherry-pick' --continue
'git cherry-pick' --quit
+'git cherry-pick' --abort
DESCRIPTION
-----------
diff --git a/Documentation/git-revert.txt b/Documentation/git-revert.txt
index b0fcabc8..b699a345 100644
--- a/Documentation/git-revert.txt
+++ b/Documentation/git-revert.txt
@@ -11,6 +11,7 @@ SYNOPSIS
'git revert' [--edit | --no-edit] [-n] [-m parent-number] [-s] <commit>...
'git revert' --continue
'git revert' --quit
+'git revert' --abort
DESCRIPTION
-----------
diff --git a/Documentation/sequencer.txt b/Documentation/sequencer.txt
index 75f8e869..5747f442 100644
--- a/Documentation/sequencer.txt
+++ b/Documentation/sequencer.txt
@@ -7,3 +7,6 @@
Forget about the current operation in progress. Can be used
to clear the sequencer state after a failed cherry-pick or
revert.
+
+--abort::
+ Cancel the operation and return to the pre-sequence state.
diff --git a/builtin/revert.c b/builtin/revert.c
index f5ba67a5..70a5fbb6 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -40,7 +40,12 @@ static const char * const cherry_pick_usage[] = {
};
enum replay_action { REVERT, CHERRY_PICK };
-enum replay_subcommand { REPLAY_NONE, REPLAY_REMOVE_STATE, REPLAY_CONTINUE };
+enum replay_subcommand {
+ REPLAY_NONE,
+ REPLAY_REMOVE_STATE,
+ REPLAY_CONTINUE,
+ REPLAY_ROLLBACK
+};
struct replay_opts {
enum replay_action action;
@@ -135,9 +140,11 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)
const char *me = action_name(opts);
int remove_state = 0;
int contin = 0;
+ int rollback = 0;
struct option options[] = {
OPT_BOOLEAN(0, "quit", &remove_state, "end revert or cherry-pick sequence"),
OPT_BOOLEAN(0, "continue", &contin, "resume revert or cherry-pick sequence"),
+ OPT_BOOLEAN(0, "abort", &rollback, "cancel revert or cherry-pick sequence"),
OPT_BOOLEAN('n', "no-commit", &opts->no_commit, "don't automatically commit"),
OPT_BOOLEAN('e', "edit", &opts->edit, "edit the commit message"),
OPT_NOOP_NOARG('r', NULL),
@@ -173,6 +180,7 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)
verify_opt_mutually_compatible(me,
"--quit", remove_state,
"--continue", contin,
+ "--abort", rollback,
NULL);
/* Set the subcommand */
@@ -180,6 +188,8 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)
opts->subcommand = REPLAY_REMOVE_STATE;
else if (contin)
opts->subcommand = REPLAY_CONTINUE;
+ else if (rollback)
+ opts->subcommand = REPLAY_ROLLBACK;
else
opts->subcommand = REPLAY_NONE;
@@ -188,8 +198,12 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)
char *this_operation;
if (opts->subcommand == REPLAY_REMOVE_STATE)
this_operation = "--quit";
- else
+ else if (opts->subcommand == REPLAY_CONTINUE)
this_operation = "--continue";
+ else {
+ assert(opts->subcommand == REPLAY_ROLLBACK);
+ this_operation = "--abort";
+ }
verify_opt_compatible(me, this_operation,
"--no-commit", opts->no_commit,
@@ -850,7 +864,7 @@ static int create_seq_dir(void)
if (file_exists(seq_dir)) {
error(_("a cherry-pick or revert is already in progress"));
- advise(_("try \"git cherry-pick (--continue | --quit)\""));
+ advise(_("try \"git cherry-pick (--continue | --quit | --abort)\""));
return -1;
}
else if (mkdir(seq_dir, 0777) < 0)
@@ -873,6 +887,71 @@ static void save_head(const char *head)
die(_("Error wrapping up %s."), head_file);
}
+static int reset_for_rollback(const unsigned char *sha1)
+{
+ const char *argv[4]; /* reset --merge <arg> + NULL */
+ argv[0] = "reset";
+ argv[1] = "--merge";
+ argv[2] = sha1_to_hex(sha1);
+ argv[3] = NULL;
+ return run_command_v_opt(argv, RUN_GIT_CMD);
+}
+
+static int rollback_single_pick(void)
+{
+ unsigned char head_sha1[20];
+
+ if (!file_exists(git_path("CHERRY_PICK_HEAD")) &&
+ !file_exists(git_path("REVERT_HEAD")))
+ return error(_("no cherry-pick or revert in progress"));
+ if (!resolve_ref("HEAD", head_sha1, 0, NULL))
+ return error(_("cannot resolve HEAD"));
+ if (is_null_sha1(head_sha1))
+ return error(_("cannot abort from a branch yet to be born"));
+ return reset_for_rollback(head_sha1);
+}
+
+static int sequencer_rollback(struct replay_opts *opts)
+{
+ const char *filename;
+ FILE *f;
+ unsigned char sha1[20];
+ struct strbuf buf = STRBUF_INIT;
+
+ filename = git_path(SEQ_HEAD_FILE);
+ f = fopen(filename, "r");
+ if (!f && errno == ENOENT) {
+ /*
+ * There is no multiple-cherry-pick in progress.
+ * If CHERRY_PICK_HEAD or REVERT_HEAD indicates
+ * a single-cherry-pick in progress, abort that.
+ */
+ return rollback_single_pick();
+ }
+ if (!f)
+ return error(_("cannot open %s: %s"), filename,
+ strerror(errno));
+ if (strbuf_getline(&buf, f, '\n')) {
+ error(_("cannot read %s: %s"), filename, ferror(f) ?
+ strerror(errno) : _("unexpected end of file"));
+ goto fail;
+ }
+ if (get_sha1_hex(buf.buf, sha1) || buf.buf[40] != '\0') {
+ error(_("stored pre-cherry-pick HEAD file '%s' is corrupt"),
+ filename);
+ goto fail;
+ }
+ if (reset_for_rollback(sha1))
+ goto fail;
+ strbuf_release(&buf);
+ fclose(f);
+ return 0;
+fail:
+ strbuf_release(&buf);
+ fclose(f);
+ return -1;
+}
+
static void save_todo(struct commit_list *todo_list, struct replay_opts *opts)
{
const char *todo_file = git_path(SEQ_TODO_FILE);
@@ -977,6 +1056,8 @@ static int pick_revisions(struct replay_opts *opts)
remove_sequencer_state(1);
return 0;
}
+ if (opts->subcommand == REPLAY_ROLLBACK)
+ return sequencer_rollback(opts);
if (opts->subcommand == REPLAY_CONTINUE) {
if (!file_exists(git_path(SEQ_TODO_FILE)))
return error(_("No %s in progress"), action_name(opts));
diff --git a/t/t3510-cherry-pick-sequence.sh b/t/t3510-cherry-pick-sequence.sh
index bb67cdcb..63297ad6 100755
--- a/t/t3510-cherry-pick-sequence.sh
+++ b/t/t3510-cherry-pick-sequence.sh
@@ -6,7 +6,7 @@ test_description='Test cherry-pick continuation features
+ picked: rewrites foo to c
+ unrelatedpick: rewrites unrelated to reallyunrelated
+ base: rewrites foo to b
- + initial: writes foo as a, unrelated as unrelated
+ + initial: writes foo as a, unrelated as unrelated, bar as bar
'
@@ -19,9 +19,16 @@ pristine_detach () {
git clean -d -f -f -q -x
}
+test_cmp_rev () {
+ git rev-parse --verify "$1" >expect.rev &&
+ git rev-parse --verify "$2" >actual.rev &&
+ test_cmp expect.rev actual.rev
+}
+
test_expect_success setup '
echo unrelated >unrelated &&
- git add unrelated &&
+ echo bar >bar &&
+ git add unrelated bar &&
test_commit initial foo a &&
test_commit base foo b &&
test_commit unrelatedpick unrelated reallyunrelated &&
@@ -75,6 +82,11 @@ test_expect_success '--quit does not complain when no cherry-pick is in progress
git cherry-pick --quit
'
+test_expect_success '--abort requires cherry-pick in progress' '
+ pristine_detach initial &&
+ test_must_fail git cherry-pick --abort
+'
+
test_expect_success '--quit cleans up sequencer state' '
pristine_detach initial &&
test_must_fail git cherry-pick base..picked &&
@@ -88,6 +100,7 @@ test_expect_success 'cherry-pick --reset (another name for --quit)' '
OBJID
:100644 100644 OBJID OBJID M unrelated
OBJID
+ :000000 100644 OBJID OBJID A bar
:000000 100644 OBJID OBJID A foo
:000000 100644 OBJID OBJID A unrelated
EOF
@@ -103,6 +116,79 @@ test_expect_success 'cherry-pick --reset (another name for --quit)' '
test_cmp expect actual
'
+test_expect_success '--abort to cancel multiple cherry-pick' '
+ pristine_detach initial &&
+ test_must_fail git cherry-pick base..anotherpick &&
+ git cherry-pick --abort &&
+ test_path_is_missing .git/sequencer &&
+ test_cmp_rev initial HEAD &&
+ git update-index --refresh &&
+ git diff-index --exit-code HEAD
+'
+
+test_expect_success '--abort to cancel single cherry-pick' '
+ pristine_detach initial &&
+ test_must_fail git cherry-pick picked &&
+ git cherry-pick --abort &&
+ test_path_is_missing .git/sequencer &&
+ test_cmp_rev initial HEAD &&
+ git update-index --refresh &&
+ git diff-index --exit-code HEAD
+'
+
+test_expect_success 'cherry-pick --abort to cancel multiple revert' '
+ pristine_detach anotherpick &&
+ test_must_fail git revert base..picked &&
+ git cherry-pick --abort &&
+ test_path_is_missing .git/sequencer &&
+ test_cmp_rev anotherpick HEAD &&
+ git update-index --refresh &&
+ git diff-index --exit-code HEAD
+'
+
+test_expect_success 'revert --abort works, too' '
+ pristine_detach anotherpick &&
+ test_must_fail git revert base..picked &&
+ git revert --abort &&
+ test_path_is_missing .git/sequencer &&
+ test_cmp_rev anotherpick HEAD
+'
+
+test_expect_success '--abort to cancel single revert' '
+ pristine_detach anotherpick &&
+ test_must_fail git revert picked &&
+ git revert --abort &&
+ test_path_is_missing .git/sequencer &&
+ test_cmp_rev anotherpick HEAD &&
+ git update-index --refresh &&
+ git diff-index --exit-code HEAD
+'
+
+test_expect_success '--abort keeps unrelated change, easy case' '
+ pristine_detach initial &&
+ echo changed >expect &&
+ test_must_fail git cherry-pick base..anotherpick &&
+ echo changed >bar &&
+ git cherry-pick --abort &&
+ test_cmp expect bar
+'
+
+test_expect_success '--abort refuses to clobber unrelated change, harder case' '
+ pristine_detach initial &&
+ echo changed >expect &&
+ test_must_fail git cherry-pick base..anotherpick &&
+ echo changed >unrelated &&
+ test_must_fail git cherry-pick --abort &&
+ test_cmp expect unrelated &&
+ git rev-list HEAD >log &&
+ test_line_count = 2 log &&
+ test_must_fail git update-index --refresh &&
+
+ git checkout unrelated &&
+ git cherry-pick --abort &&
+ test_cmp_rev initial HEAD
+'
+
test_expect_success 'cherry-pick cleans up sequencer state when one commit is left' '
pristine_detach initial &&
test_must_fail git cherry-pick base..picked &&
@@ -121,12 +207,23 @@ test_expect_success 'cherry-pick cleans up sequencer state when one commit is le
OBJID
:100644 100644 OBJID OBJID M unrelated
OBJID
+ :000000 100644 OBJID OBJID A bar
:000000 100644 OBJID OBJID A foo
:000000 100644 OBJID OBJID A unrelated
EOF
test_cmp expect actual
'
+test_expect_failure '--abort after last commit in sequence' '
+ pristine_detach initial &&
+ test_must_fail git cherry-pick base..picked &&
+ git cherry-pick --abort &&
+ test_path_is_missing .git/sequencer &&
+ test_cmp_rev initial HEAD &&
+ git update-index --refresh &&
+ git diff-index --exit-code HEAD
+'
+
test_expect_success 'cherry-pick does not implicitly stomp an existing operation' '
pristine_detach initial &&
test_must_fail git cherry-pick base..anotherpick &&
@@ -168,6 +265,7 @@ test_expect_success '--continue continues after conflicts are resolved' '
OBJID
:100644 100644 OBJID OBJID M unrelated
OBJID
+ :000000 100644 OBJID OBJID A bar
:000000 100644 OBJID OBJID A foo
:000000 100644 OBJID OBJID A unrelated
EOF
--
1.7.8.rc3
^ permalink raw reply related
* [PATCH 6/3] revert: remove --reset compatibility option
From: Jonathan Nieder @ 2011-11-22 11:20 UTC (permalink / raw)
To: Ramkumar Ramachandra
Cc: git, Christian Couder, Martin von Zweigbergk, Phil Hord,
Jay Soffian
In-Reply-To: <20111122111207.GA7399@elie.hsd1.il.comcast.net>
Remove the "git cherry-pick --reset" option, which has a different
preferred spelling nowadays ("--quit"). Luckily the old --reset name
was not around long enough for anyone to get used to it.
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
Thanks for reading.
builtin/revert.c | 3 ---
t/t3510-cherry-pick-sequence.sh | 4 ++--
2 files changed, 2 insertions(+), 5 deletions(-)
diff --git a/builtin/revert.c b/builtin/revert.c
index 70a5fbb6..0c61668b 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -154,9 +154,6 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)
OPT_STRING(0, "strategy", &opts->strategy, "strategy", "merge strategy"),
OPT_CALLBACK('X', "strategy-option", &opts, "option",
"option for merge strategy", option_parse_x),
- { OPTION_BOOLEAN, 0, "reset", &remove_state, NULL,
- "alias for --quit (deprecated)",
- PARSE_OPT_HIDDEN | PARSE_OPT_NOARG },
OPT_END(),
OPT_END(),
OPT_END(),
diff --git a/t/t3510-cherry-pick-sequence.sh b/t/t3510-cherry-pick-sequence.sh
index 63297ad6..f28a4e8a 100755
--- a/t/t3510-cherry-pick-sequence.sh
+++ b/t/t3510-cherry-pick-sequence.sh
@@ -94,7 +94,7 @@ test_expect_success '--quit cleans up sequencer state' '
test_path_is_missing .git/sequencer
'
-test_expect_success 'cherry-pick --reset (another name for --quit)' '
+test_expect_success '--quit keeps HEAD and conflicted index intact' '
pristine_detach initial &&
cat >expect <<-\EOF &&
OBJID
@@ -105,7 +105,7 @@ test_expect_success 'cherry-pick --reset (another name for --quit)' '
:000000 100644 OBJID OBJID A unrelated
EOF
test_must_fail git cherry-pick base..picked &&
- git cherry-pick --reset &&
+ git cherry-pick --quit &&
test_path_is_missing .git/sequencer &&
test_must_fail git update-index --refresh &&
{
--
1.7.8.rc3
^ permalink raw reply related
* How to deal with mixed tree?
From: Pascal Obry @ 2011-11-22 11:21 UTC (permalink / raw)
To: Git Mailing List
A project P1 is under Git.
A project P2 is under Subversion.
P2 in fact replace a sub-directory (say SD) in P1. The project P2 is a
replacement (extension) of the code in P1.
How to deal with this?
If you clone P1, remove SD in P1 to replace it by P2:
$ git diff
-> will display all the changes in SD, is there a way to avoid that?
$ git merge (between branches) will try to merge changes in SD
I'd like to really tell Git to ignore SD as if it was not there. Any solution?
Thanks,
Pascal.
--
--|------------------------------------------------------
--| Pascal Obry Team-Ada Member
--| 45, rue Gabriel Peri - 78114 Magny Les Hameaux FRANCE
--|------------------------------------------------------
--| http://www.obry.net
--| "The best way to travel is by means of imagination"
--|
--| gpg --keyserver wwwkeys.pgp.net --recv-key C1082595
^ permalink raw reply
* Re: [PATCH v2 0/3] Re: cherry-pick/revert error messages
From: Jonathan Nieder @ 2011-11-22 11:27 UTC (permalink / raw)
To: Ramkumar Ramachandra
Cc: git, Christian Couder, Martin von Zweigbergk, Phil Hord,
Jay Soffian
In-Reply-To: <20111122111207.GA7399@elie.hsd1.il.comcast.net>
Errata:
Jonathan Nieder wrote:
> May the series can provide some amusement.
"Maybe".
[...]
> Jonathan Nieder (3):
> revert: rename --reset option to --quit
> revert: rearrange pick_revisions() for clarity
> revert: improve error message for cherry-pick during cherry-pick
I took these from pu before tweaking them, so in a sense it's
appropriate that Junio's sign-off is there, but I do not mean to claim
he looked at and acked the tweaked form. I should have just left my
sign-off.
Sorry for the noise.
^ permalink raw reply
* Re: How to deal with mixed tree?
From: Holger Hellmuth @ 2011-11-22 11:35 UTC (permalink / raw)
To: Pascal Obry; +Cc: Git Mailing List
In-Reply-To: <CA+9ZNic-9U0nP1NGL0shUijnxdhaoEHwyxWM8rOzR2B6Qjq6zQ@mail.gmail.com>
On 22.11.2011 12:21, Pascal Obry wrote:
> A project P1 is under Git.
>
> A project P2 is under Subversion.
>
> P2 in fact replace a sub-directory (say SD) in P1. The project P2 is a
> replacement (extension) of the code in P1.
>
> How to deal with this?
Remove SD in P1, make a logical link from P2 to SD, add SD to
.git/info/exclude
(see "Bug report - local (and git ignored) file silently removed after
checkout" on the mailing list why exclude is better than .gitignore at
the moment)
^ permalink raw reply
* bug, Git, Windows XP, uninstallation
From: Napadovsky Vyacheslav @ 2011-11-22 13:43 UTC (permalink / raw)
To: git
Dear, Git mailing list.
Sorry, if I've sent this message to wrong address, I actually don't
know, where I should send it.
I have a problem with Uninstalling Git for Windows. For installation
I've used this url:
http://code.google.com/p/msysgit/downloads/detail?name=Git-1.7.7.1-preview20111027.exe&can=3&q=
During installation I haven't changed any settings (all was keeped default).
My OS is Windows XP Professional, Russian.
I've tried to reinstall and then uninstall it again - same error.
There error during uninstallation is on the picture here:
http://img42.imageshack.us/img42/8979/gituninstallation.jpg
Thank you.
Best regards,
Napadovskiy Vyacheslav.
^ permalink raw reply
* Git ticket / issue tracking ERA: Git shouldn't allow to push a new branch called HEAD
From: Daniele Segato @ 2011-11-22 14:04 UTC (permalink / raw)
To: Git Mailing List; +Cc: Junio C Hamano, Jeff King
On Mon, 2011-11-14 at 15:26 -0500, Jeff King wrote:
On Mon, Nov 14, 2011 at 12:22:59PM -0800, Junio C Hamano wrote:
>
> > Jeff King <peff@peff.net> writes:
> >
> > > So one solution is to block fetching of remote branches called
HEAD
> > > (which I would be OK with). But another is...
> > > ... Obviously there's a lot more to it than just tweaking the
default fetch
> > > refspecs. The ref lookup rules need to be changed to take this
into
> > > account. There was some discussion about this over the summer
(under the
> > > subject of possible "1.8.0" changes), but I don't think any work
has
> > > been done.
> >
> > I would say discussing and ironing out the kinks of the design
counts as
> > work, but I agree nobody was seriously interested in laying out a
sensible
> > transition plan and discussion died out before anything concrete
happened.
>
> Yeah, I should have said "...has been done since then".
>
> > Regardless of the layout chanage, which probably is a 2.X topic, I
think a
> > good first step would be to start forbidding anything that ends with
_?HEAD
> > as a branch or tag name, on top of Michael's "enforce the refname
rules more
> > vigorously when a ref is created" series.
>
> Agreed. Changing the layout is a long-term fix, and I think
disallowing
> HEAD is a reasonable stop-gap measure.
Hi,
I pretty much agree with you that this is the right solution in the
long-term.
But how are you going to remember this?
Is there an issue tracking?
Do you use some other kind of ticketing system?
Since the *official* repo is now hosted on github, why don't making use
of the github ticketing system? [1]
You probably already discussed this tons of times but I can't figure out
how you are gonna remember to do something if you don't have a roadmap
and a ticketing system that allow you to trace the bugs and features.
Do you have to dig into the mailing list archive to look for the
previous discussion?
This is actually just out of curiosity, sorry if this hurt someones
feeling.
Thanks and regards,
Daniele Segato
[1] according to http://git-scm.com/ the link on "Git source repository"
is https://github.com/gitster/git
^ permalink raw reply
* Re: bug, Git, Windows XP, uninstallation
From: Konstantin Khomoutov @ 2011-11-22 14:04 UTC (permalink / raw)
To: Napadovsky Vyacheslav; +Cc: git
In-Reply-To: <CA+hB07RJ6guYR6wGG=-88wjAdMd4_LR0Qtpd=tAWqv+tbH6Mtw@mail.gmail.com>
On Tue, 22 Nov 2011 17:43:43 +0400
Napadovsky Vyacheslav <napadovskiy@gmail.com> wrote:
> Sorry, if I've sent this message to wrong address, I actually don't
> know, where I should send it.
>
> I have a problem with Uninstalling Git for Windows. For installation
> I've used this url:
> http://code.google.com/p/msysgit/downloads/detail?name=Git-1.7.7.1-preview20111027.exe&can=3&q=
http://groups.google.com/group/msysgit/browse_thread/thread/f3c64ced53a3ae9d
^ permalink raw reply
* Re: How to deal with mixed tree?
From: Pascal Obry @ 2011-11-22 14:20 UTC (permalink / raw)
To: Holger Hellmuth; +Cc: Git Mailing List
In-Reply-To: <4ECB8917.8010305@ira.uka.de>
Holger,
> Remove SD in P1, make a logical link from P2 to SD, add SD to
> .git/info/exclude
Thanks for your quick reply. That's what I have tested but...
> (see "Bug report - local (and git ignored) file silently removed after
> checkout" on the mailing list why exclude is better than .gitignore at
> the moment)
Seems like this is working only if file names are different. This is not
my case as the replacement is very similar. With the following script
I'm expecting empty status and no diff:
<<
#!/bin/sh
# create sd (directory that will replace src2)
mkdir sd
echo sd1 > sd/file1
echo sd2 > sd/file2
# create Git repo
mkdir repo
cd repo
git init
mkdir src1
mkdir src2
echo file > src1/file
echo 3 > src2/file3
git add .
git ci -a -m "first"
# let's replace src2 by sd
rm -fr src2
# ln -s ../sd src2
cp -r ../sd src2
# make sure src2 is excluded
echo 'src2/*' >> .git/info/exclude
# the following output should be clean
echo '============== Status'
git status
echo '============== Diff'
git diff
>>
But the output is actually:
Initialized empty Git repository in /home/obry/tmp/repo/.git/
[master (root-commit) 527c7a1] first
2 files changed, 2 insertions(+), 0 deletions(-)
create mode 100644 src1/file
create mode 100644 src2/file3
cp: cannot stat `../src': No such file or directory
============== Status
# On branch master
# Changes not staged for commit:
# (use "git add/rm <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working
directory)
#
# deleted: src2/file3
#
no changes added to commit (use "git add" and/or "git commit -a")
============== Diff
diff --git a/src2/file3 b/src2/file3
deleted file mode 100644
index 00750ed..0000000
--- a/src2/file3
+++ /dev/null
@@ -1 +0,0 @@
-3
Any other idea?
Thanks.
--
--|------------------------------------------------------
--| Pascal Obry Team-Ada Member
--| 45, rue Gabriel Peri - 78114 Magny Les Hameaux FRANCE
--|------------------------------------------------------
--| http://www.obry.net - http://v2p.fr.eu.org
--| "The best way to travel is by means of imagination"
--|
--| gpg --keyserver keys.gnupg.net --recv-key F949BD3B
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox