* Re: why is git destructive by default? (i suggest it not be!)
From: Jeff King @ 2008-06-24 7:24 UTC (permalink / raw)
To: David Jeske; +Cc: Avery Pennarun, Nicolas Pitre, git
In-Reply-To: <willow-jeske-01l5izRzFEDjCdyL>
On Tue, Jun 24, 2008 at 06:35:16AM -0000, David Jeske wrote:
> If branches are "temporary tags" how do I see the actual code they had
> working in their branch before they merged it?
You look at the shape of the history. But if it is really an important
event for you to say "this was the state right before some merge of
interest", then by all means, tag it with a real tag. Or don't delete
the branch.
Have you tried running gitk on the kernel or git repositories?
> I'm reading about rebase, and it sounds like something I would want to
> forever disallow on my git repository, because it looks like it
> rewrites history and makes it impossible to get to the state of the
> tree they actually had working before the merge. However, something
> you say below both clarifies and confuses this.
It does throw away the state before the rebase (well, there is no longer
a pointer to it; it is still recoverable via the reflog). But for most
push/pull collaboration, you probably want to be using merge. Rebase is
more useful for people who are more accustomed to a patch-based
workflow.
> > The end result is that even if you delete the source branch after
> > doing a merge, nothing is actually lost.
>
> ..and what if you never merge? That branch-pointer points to useful
> information about a development attempt, but it was never merged.
> (imagine a different development path was taken) They never created a
> tag because it's not clear when that work was "done" (unlike a
> release, which is much more well understood). What prevents someone
> from deleting the branch-pointer or moving it to a different part of
> the tree, causing that set of changes to be a dangling ref lost in a
> sea of refs. Later when someone goes back looking for it, how would
> they ever find it in a sea of tens of thousands of checkins?
If it's not merged, then don't delete the branch pointer! And "git
branch -d" will even refuse to do the deletion, unless you force it with
"git branch -D".
And keep in mind that when you clone repos, you clone the branch
pointer. So if you have a centralized server that your developers push
and pull from, a stray "git branch -D" from one developer _doesn't_ ruin
it for the rest of them. All that does is delete the branch from their
local repo, but it still exists in the central repo and for all of the
other developers. But it's not clear to me what sort of developer
topology you're interested in.
> Before I set the GC times to "100 years", there was a HUGE reason for git to
> make those branch-pointers impossible to lose, because by default if you lose
> them git actually garbage collects them and throws the diffs away after 90
> days!
I think most people are comfortable with "if I have an unmerged branch,
it stays forever. If I accidentally delete my branch, I have 30 days to
pull the tip out of my reflog". Sure, it's _possible_ to lose work. But
you could also accidentally "rm -rf" your .git directory. If you want an
extra layer of protection, push your work periodically to a backup repo.
> That's sort of helpful, and sort of confusing. I think of git's branches as
> "branch pointers to the head of a linked-list of states of the tree".
More or less true (they aren't linked-list, but arbitrary DAGs --
commits can have more than one parent (i.e., a merge) and can have many
children (i.e., many people build off in different directions from one
spot)).
> If I'm understanding all that right, it's exactly the kind of
> functionality I want -- the ability to reproduce the state of all
> working history, exactly as it was when the code was actually working
> in someone's client a long time ago, before they merged it to the
> mainline. Except the standard model seems to be to let the system
> "garbage collect" all that history, and toss it away as unimportant --
> and in some cases it seems to even provide developers with ways to
> more aggressively assure garbage collection makes it disappear.
I think you are confusing two aspects of history.
There is the commit DAG, which says "at some time T, the files were at
some state S, and the commit message by author A was M". And those
commits form a chain so you can see how the state of the files
progressed. And anything that is reachable through that history will
always be kept by git, and you can always go back to any point.
But we also give particular names to some points, like "this is tag
v1.0" or "this is the head of the experimental line of development". We
call those refs. Git remembers those names until you ask it not to (by
deleting the ref). And there is a history to those names, like
"experimental was at some commit C1. Then somebody committed and it was
at C2. And then they did a git-reset and it was at C3". And that history
is encapsulated in the reflog, and is purely local to each repository
(since git is distributed, it makes no sense to talk about "where the
experimental name pointed" without talking about a specific repo).
And the ref history is what gets garbage collected. Most people are fine
with that, because they care about the actual commit history, and the
reflog is just a convenient way of saying "oops, what was happening
yesterday?" But if you really care, then by all means, set the reflog
expiration much higher.
> Am I expecting too much out of git? It doesn't really feel like a
> source control system for an organization that wants to save
> everything, forever, even when those people and trees and home
> directories disappear. It feels like a distributed patch manager that
> is much more automatic than sending around diffs, but isn't overly
> concerned with providing access to old history. (which, duh, is no
> surprise given that's what I expect it's doing for linux kernel)
Git _will_ remember content forever, _if_ you put into git. So if you
are saying "git won't remember work that employee X did after he is
gone", that isn't true. X's work will be part of the commit DAG and will
be a part of everybody's repo. If you are saying "I blew away employee
X's home directory, and he had a git repo in it, why didn't git save
that data?" then the problem is that you deleted the repo! If you are
concerned about that situation, have employee X push his work to a repo
that doesn't get deleted.
-Peff
^ permalink raw reply
* Re: git svn --add-author-from implies --use-log-author
From: Fredrik Skolmli @ 2008-06-24 7:37 UTC (permalink / raw)
To: Avery Pennarun; +Cc: Mircea Bardac, git
In-Reply-To: <32541b130806231312l679aba31ra3daac2bb634cf1b@mail.gmail.com>
On Mon, Jun 23, 2008 at 04:12:37PM -0400, Avery Pennarun wrote:
> They are separate options; --add-author-from affects dcommit (but not
> fetch or clone), while --use-log-author affects fetch and clone (but
> not dcommit). They do different things, and I can imagine wanting one
> and not the other, so having one imply the other isn't very safe.
No, I agree implying it isn't safe. The need for implying --use-log-author
when doing --add-author-from might just be me being to lazy to remember both
flags. :-)
> You can set config options for these, however:
>
> git config svn.addAuthorFrom true
> git config svn.useLogAuthor true
>
> (I actually use "git config --global" to set these on my system so
> they apply to all my git-svn repositories.)
Hm, I did not know about these config options. Are they documented somewhere?
- F
--
Regards,
Fredrik Skolmli
^ permalink raw reply
* Re: [PATCH] clone: create intermediate directories of destination repo
From: Junio C Hamano @ 2008-06-24 7:39 UTC (permalink / raw)
To: Jeff King; +Cc: Brandon Casey, Daniel Barkalow, zuh, git
In-Reply-To: <20080624055022.GC19224@sigill.intra.peff.net>
Jeff King <peff@peff.net> writes:
> The shell version used to use "mkdir -p" to create the repo path, but
> the C version just calls "mkdir". Let's replicate the old behavior. In
> this case we can simply create the directories leading up to the git
> dir. If it's a bare repo, then that is everything that init_db wants
> ahead of time. If it isn't bare, then the worktree contains the git dir,
> so we create the worktree.
Clever ;-)
> The big difference is that safe_create_leading_directories will do an
> adjust_shared_perm on the result. I don't think that should be a
> problem, but it is a difference.
This early in the code you would not have read anything that triggers
"shared" so it probably is not a problem, I would think.
^ permalink raw reply
* Re: [RFC] Re: Convert 'git blame' to parse_options()
From: Pierre Habouzit @ 2008-06-24 7:50 UTC (permalink / raw)
To: Junio C Hamano
Cc: Linus Torvalds, Jeff King, Johannes Schindelin, Git Mailing List
In-Reply-To: <7v8wwvg15z.fsf@gitster.siamese.dyndns.org>
[-- Attachment #1: Type: text/plain, Size: 1392 bytes --]
On Mon, Jun 23, 2008 at 11:51:52PM +0000, Junio C Hamano wrote:
> Pierre Habouzit <madcoder@debian.org> writes:
>
> > Unrelated but worth to note: many parse_options users just don't care
> > about argv[0] and having it kept each time would rather be a pain for
> > them (they would need to call argv++, argc-- themselves).
>
> Not necessarily. If they were parsing by hand, they were written to deal
> with the fact that argv[0] is not the program argument (iow, they start
> counting from one). And before and after calling parse_options(), they
> need to change that assumption anyway, because parse_options() makes
> argv[0] disappear.
Sure, but for _some_ commands it's easier not having to argv++,
argc--. The patch I sent just allow the caller to ask parse_options to
keep argv[0] in place or not, so that one can cascade parsers if wanted.
Note that with the series I sent, it's less useful to cascade option
parser, you rather mix more of them in your step by step parser. But oh
well, it's a useful feature anyways. We can even make it default, my
sole concern what that NULL-terminated thing, and now that I know we can
count on it, then, well, I absolutely don't care :)
--
·O· Pierre Habouzit
··O madcoder@debian.org
OOO http://www.madism.org
[-- Attachment #2: Type: application/pgp-signature, Size: 197 bytes --]
^ permalink raw reply
* Re: why is git destructive by default? (i suggest it not be!)
From: Jakub Narebski @ 2008-06-24 7:54 UTC (permalink / raw)
To: Avery Pennarun; +Cc: David Jeske, Nicolas Pitre, git
In-Reply-To: <32541b130806232220r292d691cn5bf5f9976126aa29@mail.gmail.com>
It looks like for some reason not all messages made it to git mailing
list, at least when using GMane to read git mailing list. Strange...
"Avery Pennarun" <apenwarr@gmail.com> writes:
> On 6/24/08, David Jeske <jeske@google.com> wrote:
>> I moved a branch around and then deleted it, and I don't see any
>> record in the reflog of where it was, or that it ever was.
Deleting branch (BTW. git prints warning when deleting branch can
result in [temporary] loss of [easy access to] some commits) deletes
its reflog[*1*], but you can still use HEAD reflog ("what was checked
out" reflog).
>> Am I missing something about how branches are used? I see some
>> language in "git tag" about how attempts are made to assure that
>> others can't move around semi-immutable tags during push, but I
>> don't see any such language about branches. What prevents someone
>> from accidentally deleting an old branch that nobody is watching,
>> but is important to the history and then not noticing as gc
>> silently deletes the old deltas?
BTW. branches _deletions_ are not by default transferred (even if
using globbing refspecs, which is not default); you have to use
"git remote prune <remote nick>" to remove remote-tracking branches
which track branches that got deleted on remote.
Besides nobody and nothing can fully protect you from your stupidity.
You can "accidentally" do 'rm -rf .git' for example :-/
>> I've had need to pull out versions several years old multiple times
>> in my career, so this is the kind of thing I'm thinking about.
The answer is: don't delete branches accidentally ;-).
Seriously, in any sane workflow you have several long lasting
branches, be it 'maint', 'master', 'next' or be it 'maintenance',
'stable'/'mainline'/'trunk', 'devel', into whose you merge in
[temporary, short lived] topic branches when topic is ready for
inclusion. And you NEVER delete such branches (git can't protect you
from deletion any more than Linux can protect you if you do "rm -rf ~").
Any commit for whose there is parentage line from one of those
long-lived "development" branches would be protected from pruning
during git-gc run.
> git branches are actually a very different concept from branches in,
> say, subversion.
>
> In subversion, a branch is normally created so that you can do
> parallel development, and then you merge whole batches of changes
> (with 'svn merge') from one branch into another. When you do this,
> you create a single new commit in the destination branch that contains
> *all* the changes. So if you want to look back in history to see who
> did which part of the change for what reason, you have to go back to
> the branch you merged *from*. Thus, it's very important in subversion
> that old branches never disappear.
>
> git's philosophy is different. Branches are really just "temporary
> tags".
I'd rather say thay branches (refs/heads branches) are "growth points"
of graph (diagram) of revisions (versions). (This graph is called DAG
in git documentation, because it is Directed Acyclic Graph).
But it is true that in git branches are just _pointers_ to the DAG
of commits. All data is kept in the content addressed object database
which is git repo storage, and parentage links are contained in commit
objects.
> A merge operation doesn't just copy data from one branch to
> another: it actually joins the two histories together, so you can then
> trace back through the exact history of the merged branches, commit by
> commit. "git log" will show each checkin to *either* branch
> individually, instead of just one big "merge" checkin.
Let me help explain that using some ASCII-art diagram. You need to
use fixed-width (non-proportional) font to view it correctly. Time
flows from the left to right.
Let's assume that we have the following state: some history on branch
'master':
object database refs information
/-------------------\ /---------------------\
.<---.<---.<---A <--- master <=== HEAD
For the commits the "<---" arrow means that commit on the right side
of arrow has commit on the left hand side of arrow as its parent
(saved in the multi-valued "parent" field in the commit object). For
the references "<---" arrow means that branch master points to given
commit, and "<===" means symbolic reference, i.e. that ref points to
given branch (you can think of it as symlink, and it was some time ago
implemented as such).
Now assume that we created new branch 'test', and we have comitted
some revisions being on it:
.<---.<---.<---A <--- master
\
\-B<---C <--- test <=== HEAD
Let's assume that we, or somebody else, did some work on 'master'
branch (to not confuse you with the "fast-formward" issue):
.<---.<---.<---A<---X<---Y <--- master
\
\--B<---C <--- test <=== HEAD
Now we have finished feature which we tried to develop in 'test', so
we merge changes back to 'master':
.<---.<---.<---A<---X<---Y<---M <--- master <=== HEAD
\ /
\--B<---C<-/ <--- test
Note how merge commit 'M' has two parents.
Now if we were to delete branch 'test' now:
.<---.<---.<---A<---X<---Y<---M <--- master [<=== HEAD]
\ /
\--B<---C<-/
it is only pointer that gets deleted (and reflog[*1*]). All commits
which were on this branch are 'reachable', so they never would get
deleted, even if [HEAD] reflog expires[*2*].
> The end result is that even if you delete the source branch after
> doing a merge, nothing is actually lost. Thus, there's no reason for
> git to try to make branches impossible to lose, as they are in svn.
> In the event that you really needed that branch pointer, it's in the
> reflog, as a few people have pointed out.
s/in the reflog/in the HEAD reflog/.
See above for explanation with pictures (or if you want some graphics,
take a look at presentations linked from GitLinks page and/or
GitDocumentation page on git wiki, http://git.or.cz/gitwiki/).
HTH
Footnotes:
==========
[*1*] There was an effort to create some sort of 'Attic' / 'trash can'
for deleted reflogs, but I guess it got stalled. There is techical
issue caused by the fact that reflogs are stored as files, and you can
have so caled file<->directory conflict, when you deleted branch
'foo', and created branch 'foo/bar'.
[*2*] You can always write "never" as time to expire, and it even
works now ;-)
--
Jakub Narebski
Poland
ShadeHawk on #git
^ permalink raw reply
* Re: What's cooking in git.git (topics)
From: Junio C Hamano @ 2008-06-24 7:59 UTC (permalink / raw)
To: Miklos Vajna; +Cc: git
In-Reply-To: <20080621121429.GI29404@genesis.frugalware.org>
Miklos Vajna <vmiklos@frugalware.org> writes:
> On Sat, Jun 21, 2008 at 02:44:50AM -0700, Junio C Hamano <gitster@pobox.com> wrote:
>> * nd/dashless (Wed Nov 28 23:21:57 2007 +0700) 1 commit
>> + Move all dashed-form commands to libexecdir
>>
>> Scheduled for 1.6.0.
>>
>> * jc/dashless (Sat Dec 1 22:09:22 2007 -0800) 2 commits
>> - Prepare execv_git_cmd() for removal of builtins from the
>> filesystem
>> - git-shell: accept "git foo" form
>>
>> We do not plan to remove git-foo form completely from the filesystem at
>> this point, but git-shell may need to be updated.
>
> I may be wrong, but given that git-upload-pack/receive-pack is now not
> in PATH, I think it will be problematic to do a pull/push in case the
> server runs next, the client is 1.5.6 and the user has git-shell as
> shell, for example.
The idea of the "shell: accept 'git foo' form" patch is that as long as
the server end consistently use the same version (i.e. git-shell is from
'next' and it knows where the rest of git is installed), things should
work fine. I've merged them to 'next' and pushed it out so that you can
try it.
I do not use git-shell in production setting (I do have one user account
whose login shell is git-shell from 'next' I use purely for testing), and
I do not know how much use it has seen in the real world. My cursory
sanity-check ("cvs -d :ext:thatuser@myhost:/path/ co --help", and "git
ls-remote ssh://thatuser@myhost/path/") seems to be Ok with $(bindir)
that has only git, gitk and nothing else.
^ permalink raw reply
* Re: [PATCH] clone: create intermediate directories of destination repo
From: Jeff King @ 2008-06-24 8:04 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Brandon Casey, Daniel Barkalow, zuh, git
In-Reply-To: <7v3an3e0xv.fsf@gitster.siamese.dyndns.org>
On Tue, Jun 24, 2008 at 12:39:40AM -0700, Junio C Hamano wrote:
> > The shell version used to use "mkdir -p" to create the repo path, but
> > the C version just calls "mkdir". Let's replicate the old behavior. In
> > this case we can simply create the directories leading up to the git
> > dir. If it's a bare repo, then that is everything that init_db wants
> > ahead of time. If it isn't bare, then the worktree contains the git dir,
> > so we create the worktree.
>
> Clever ;-)
I am worried that it is too clever. I didn't see an obvious way for
work_tree and git_dir to not have that property, but I think it is still
worth somebody double-checking.
-Peff
^ permalink raw reply
* Re: why is git destructive by default? (i suggest it not be!)
From: David Jeske @ 2008-06-24 7:31 UTC (permalink / raw)
To: Jeff King; +Cc: Avery Pennarun, Nicolas Pitre, git
In-Reply-To: <20080624072455.GF19224@sigill.intra.peff.net>
-- Jeff King wrote:
> I think you are confusing two aspects of history.
>
> There is the commit DAG, which says "at some time T, the files were at
> some state S, and the commit message by author A was M". And those
> commits form a chain so you can see how the state of the files
> progressed. And anything that is reachable through that history will
okay.
> always be kept by git, and you can always go back to any point.
..are you saying that if I reset --hard, or delete a branch ref, or do a
rebase, and then do a GC beyond the GC timeout, that git will NEVER throw away
any of those DAGs? (the actual source diffs committed)
> And the ref history is what gets garbage collected. Most people are fine
> with that, because they care about the actual commit history, and the
> reflog is just a convenient way of saying "oops, what was happening
> yesterday?" But if you really care, then by all means, set the reflog
> expiration much higher.
My (possibly flawed) understanding was that it drops any DAG sections that are
not referenced by valid refs which are older than the GC timeout.
It came from wording like this in the docs:
"The optional configuration variable gc.reflogExpireUnreachable
can be set to indicate how long historical reflog entries which
are not part of the current branch should remain available in
this repository. These types of entries are generally created
as a result of using git commit --amend or git rebase and are the
commits prior to the amend or rebase occurring. Since
these changes are not part of the current project most users
^^^^^^^^^^^^^
will want to expire them sooner. This option defaults to 30 days."
In the above, I resolve "these changes" to "commits prior to the amend" in the
previous sentence.
"git-gc tries very hard to be safe about the garbage it collects.
In particular, it will keep not only objects referenced by your
current set of branches and tags, but also objects referenced by
the index, remote tracking branches, refs saved by
git-filter-branch(1) in refs/original/, or reflogs (which may
references commits in branches that were later amended or rewound)."
In the above, I resolve "keep .. only objects referenced by your current set of
branches and tags [and some other stuff]" to "commmits in the DAG pointed to by
refs [and other stuff]".
Are you saying this GC process will never collect source diffs in the DAG?
^ permalink raw reply
* Re: why is git destructive by default? (i suggest it not be!)
From: David Jeske @ 2008-06-24 7:31 UTC (permalink / raw)
To: Jeff King; +Cc: Avery Pennarun, Nicolas Pitre, git
In-Reply-To: <20080624072455.GF19224@sigill.intra.peff.net>
-- Jeff King wrote:
> I think you are confusing two aspects of history.
>
> There is the commit DAG, which says "at some time T, the files were at
> some state S, and the commit message by author A was M". And those
> commits form a chain so you can see how the state of the files
> progressed. And anything that is reachable through that history will
okay.
> always be kept by git, and you can always go back to any point.
..are you saying that if I reset --hard, or delete a branch ref, or do a
rebase, and then do a GC beyond the GC timeout, that git will NEVER throw away
any of those DAGs? (the actual source diffs committed)
> And the ref history is what gets garbage collected. Most people are fine
> with that, because they care about the actual commit history, and the
> reflog is just a convenient way of saying "oops, what was happening
> yesterday?" But if you really care, then by all means, set the reflog
> expiration much higher.
My (possibly flawed) understanding was that it drops any DAG sections that are
not referenced by valid refs which are older than the GC timeout.
It came from wording like this in the docs:
"The optional configuration variable gc.reflogExpireUnreachable
can be set to indicate how long historical reflog entries which
are not part of the current branch should remain available in
this repository. These types of entries are generally created
as a result of using git commit --amend or git rebase and are the
commits prior to the amend or rebase occurring. Since
these changes are not part of the current project most users
^^^^^^^^^^^^^
will want to expire them sooner. This option defaults to 30 days."
In the above, I resolve "these changes" to "commits prior to the amend" in the
previous sentence.
"git-gc tries very hard to be safe about the garbage it collects.
In particular, it will keep not only objects referenced by your
current set of branches and tags, but also objects referenced by
the index, remote tracking branches, refs saved by
git-filter-branch(1) in refs/original/, or reflogs (which may
references commits in branches that were later amended or rewound)."
In the above, I resolve "keep .. only objects referenced by your current set of
branches and tags [and some other stuff]" to "commmits in the DAG pointed to by
refs [and other stuff]".
Are you saying this GC process will never collect source diffs in the DAG?
^ permalink raw reply
* Re: [PATCH/RFC] Created git-basis and modified git-bundle to accept --stdin.
From: Jakub Narebski @ 2008-06-24 8:09 UTC (permalink / raw)
To: Adam Brewster; +Cc: git
In-Reply-To: <c376da900806231921y2d822been9cd573d509fbf78a@mail.gmail.com>
"Adam Brewster" <adam@adambrewster.com> writes:
> Git-basis is a perl script that remembers bases for use by git-bundle.
> Code from rev-parse was borrowed to allow git-bundle to handle --stdin.
I'd rather you follow git-pack-objects, and use `--revs` for the name
of this option (or even '--not --revs'). The name `--stdin` might
imply that you are providing objects names on stdin of git-bundle.
But perhaps I am worrying over nothing.
[...]
> Then you can add the objects in the bundle to the basis, so they won't
> get included in the next pack like this:
>
> $ git-basis --update my-basis < my-bundle
Why not use "$(git ls-remote my-bundle)" somewhere in the invocation
creating new bundle instead?
> I'm sure that my implementation is crap, but I think this is a useful
> idea. Anybody agree? Disagree?
Documentation, please? Especially that it looks like '--stdin' option
is a bit tricky...
--
Jakub Narebski
Poland
ShadeHawk on #git
^ permalink raw reply
* Re: What's cooking in git.git (topics)
From: Pieter de Bie @ 2008-06-24 8:12 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Miklos Vajna, git
In-Reply-To: <7vwskfclfs.fsf@gitster.siamese.dyndns.org>
On 24 jun 2008, at 09:59, Junio C Hamano wrote:
> The idea of the "shell: accept 'git foo' form" patch is that as long
> as
> the server end consistently use the same version (i.e. git-shell is
> from
> 'next' and it knows where the rest of git is installed), things should
> work fine. I've merged them to 'next' and pushed it out so that you
> can
> try it.
Any clone / push operation fails if you use current next:
Vienna:bin pieter$ git --version
git version 1.5.6.129.g274ea
Vienna:bin pieter$ git clone localhost:project/bonnenteller
Initialize bonnenteller/.git
Initialized empty Git repository in /opt/git/bin/bonnenteller/.git/
Password:
bash: git-upload-pack: command not found
fatal: The remote end hung up unexpectedly
I think that is what Miklos meant. Also, I think the client sends the
command to execute on the remote side. At least for v1.5.5 clients and
before, that is "git-upload-pack". As this is not in PATH, that
command will fail on any server that runs v1.5.6 and has the libexec
dir.
- Pieter
^ permalink raw reply
* Re: why is git destructive by default? (i suggest it not be!)
From: Lea Wiemann @ 2008-06-24 8:14 UTC (permalink / raw)
To: David Jeske; +Cc: Nicolas Pitre, git
In-Reply-To: <willow-jeske-01l5e9cgFEDjCh3F>
David Jeske wrote:
> ... or we expect "human parsing of the the log" is a valid common
> user-interface for non-git developers.
As a side note, the reflog is not only a valid user interface, but an
important one: As a local developer that feeds patches to the mailing
list, I frequently change the history in my local repository (using
rebase, reset and am, or pull --rebase) to keep the commits clean when
they finally get merged upstream. I *want* and *need* at least basic
versioning for the various states my history is in.
IOW, I not only make changes to the tree and commit them to my master
branch, but I also make changes to my master branch and "commit" them to
(store them in) the reflog.
That's not an interesting use case if you're working on a branch that
other people pull from, but for a local clone it's very useful. (And
it's a feature I haven't seen in any VCSes, FWIW.)
Best,
Lea
^ permalink raw reply
* Re: why is git destructive by default? (i suggest it not be!)
From: Jeff King @ 2008-06-24 8:16 UTC (permalink / raw)
To: David Jeske; +Cc: Avery Pennarun, Nicolas Pitre, git
In-Reply-To: <willow-jeske-01l5kbGzFEDjCX3J>
On Tue, Jun 24, 2008 at 07:31:31AM -0000, David Jeske wrote:
> ..are you saying that if I reset --hard, or delete a branch ref, or do a
> rebase, and then do a GC beyond the GC timeout, that git will NEVER throw away
> any of those DAGs? (the actual source diffs committed)
No. Git keeps the reachable DAG. So if the DAG is part of development
that is merged into one of your long running branches, or if you keep
around the branch that points to it, it will never go away.
> My (possibly flawed) understanding was that it drops any DAG sections
> that are not referenced by valid refs which are older than the GC
> timeout.
Yes. So the way to "forget" about some history is to stop referencing
it. And then, after a grace period, it will be removed.
> Are you saying this GC process will never collect source diffs in the
> DAG?
No, but it will only remove unreferenced things. And things only become
unreferenced through explicit user action. So you don't have to worry
about git GCing your work unexpectedly. You do have to worry about git
GCing things you have explicitly told it to delete.
-Peff
^ permalink raw reply
* Re: What's cooking in git.git (topics)
From: Pieter de Bie @ 2008-06-24 8:16 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Miklos Vajna, Git Mailinglist
In-Reply-To: <9B8F0B10-F48D-475B-BF59-CEE94222B6E8@ai.rug.nl>
On 24 jun 2008, at 10:12, Pieter de Bie wrote:
> I think that is what Miklos meant. Also, I think the client sends
> the command to execute on the remote side. At least for v1.5.5
> clients and before, that is "git-upload-pack". As this is not in
> PATH, that command will fail on any server that runs v1.5.6 and has
> the libexec dir.
That is supposed to be "v1.5.6" and "v1.6.0" respectively.
^ permalink raw reply
* Re: why is git destructive by default? (i suggest it not be!)
From: David Jeske @ 2008-06-24 8:08 UTC (permalink / raw)
To: Jakub Narebski; +Cc: Avery Pennarun, Nicolas Pitre, git
In-Reply-To: <m3mylbl0xb.fsf@localhost.localdomain>
To re-ask the same question I asked in my last post, using your ascii
pictures...
Let's assume we're here..
.<---.<---.<---A<---X<---Y <--- master
\
\--B<---C <--- customer_A_branch <=== HEAD
And this person and everyone else moves their head pointers back to master
without merging:
.<---.<---.<---A<---X<---Y <--- master <=== HEAD
\
\--B<---C <--- customer_A_branch
Now, five years down the road, our tree looks like:
.<---A<---X<---Y<---.<--.<--.(3 years of changes)<---ZZZ<--- master <=== HEAD
\
\--B<---C <--- customer_A_branch
And someone does:
git-branch -f customer_A_branch ZZZ
To bring us to:
.<---A<---X<---Y<---.<--.(3 years of changes)<---ZZZ<--- master <=== HEAD
\ \
\--B<---C \-- customer_A_branch
..at this point, will a GC keep "B<--C", or garbage collect the commits and
throw them away?
^ permalink raw reply
* Re: why is git destructive by default? (i suggest it not be!)
From: David Jeske @ 2008-06-24 8:08 UTC (permalink / raw)
To: Jakub Narebski; +Cc: Avery Pennarun, Nicolas Pitre, git
In-Reply-To: <m3mylbl0xb.fsf@localhost.localdomain>
To re-ask the same question I asked in my last post, using your ascii
pictures...
Let's assume we're here..
.<---.<---.<---A<---X<---Y <--- master
\
\--B<---C <--- customer_A_branch <=== HEAD
And this person and everyone else moves their head pointers back to master
without merging:
.<---.<---.<---A<---X<---Y <--- master <=== HEAD
\
\--B<---C <--- customer_A_branch
Now, five years down the road, our tree looks like:
.<---A<---X<---Y<---.<--.<--.(3 years of changes)<---ZZZ<--- master <=== HEAD
\
\--B<---C <--- customer_A_branch
And someone does:
git-branch -f customer_A_branch ZZZ
To bring us to:
.<---A<---X<---Y<---.<--.(3 years of changes)<---ZZZ<--- master <=== HEAD
\ \
\--B<---C \-- customer_A_branch
..at this point, will a GC keep "B<--C", or garbage collect the commits and
throw them away?
^ permalink raw reply
* Re: [RFC] Re: Convert 'git blame' to parse_options()
From: Pierre Habouzit @ 2008-06-24 8:24 UTC (permalink / raw)
To: Junio C Hamano
Cc: Linus Torvalds, Jeff King, Johannes Schindelin, Git Mailing List
In-Reply-To: <7vzlpbeksn.fsf@gitster.siamese.dyndns.org>
[-- Attachment #1: Type: text/plain, Size: 2641 bytes --]
On Tue, Jun 24, 2008 at 12:30:48AM +0000, Junio C Hamano wrote:
> Linus Torvalds <torvalds@linux-foundation.org> writes:
>
> > On Mon, 23 Jun 2008, Linus Torvalds wrote:
> >>
> >> Umm. Helloo, reality.. There are actually very few options that take a
> >> flag for their arguments. In particular, the option parsing we really
> >> _care_ about (revision parsing - see builtin-blame.c which is exactly
> >> where I wanted to convert things) very much DOES NOT.
> >
> > Actually, I guess "--default" does, but if you try to mix that up with (a)
> > a default head that starts with a dash and (b) git-blame, you're doing
> > something pretty odd.
> >
> > And yes, "-n" does too, but if you pass it negative numbers you get what
> > you deserve.
> >
> > The point being, we really _do_ have a real-life existing case for
> > PARSE_OPT_CONTINUE_ON_UNKNOWN, which is hard to handle any other way.
> > Currently you can literally do
> >
> > git blame --since=April -b Makefile
> >
> > and while it's a totally made-up example, it's one I've picked to show
> > exactly what does *not* work with my patch that started this whole thread.
> >
> > And guess what you need to fix it?
> >
> > If you guessed PARSE_OPT_CONTINUE_ON_UNKNOWN, you win a prize.
>
> With this on top of Pierre's series, and adding PARSE_OPT_SKIP_UNKNOWN to
> the obvious place in your patch, "blame --since=April -b Makefile" would
> work.
>
> -- >8 --
> Subject: [PATCH] parse-options: PARSE_OPT_SKIP_UNKNOWN
>
> ---
> parse-options.c | 14 ++++++++++----
> parse-options.h | 1 +
> 2 files changed, 11 insertions(+), 4 deletions(-)
>
> diff --git a/parse-options.c b/parse-options.c
> index 71a8056..9f1eb65 100644
> --- a/parse-options.c
> +++ b/parse-options.c
> @@ -341,20 +341,26 @@ int parse_options(int argc, const char **argv, const struct option *options,
> struct parse_opt_ctx_t ctx;
>
> parse_options_start(&ctx, argc, argv, flags);
> +
> + again:
> switch (parse_options_step(&ctx, options, usagestr)) {
> case PARSE_OPT_HELP:
> exit(129);
> case PARSE_OPT_DONE:
> break;
> default: /* PARSE_OPT_UNKNOWN */
> - if (ctx.argv[0][1] == '-') {
> + if (flags & PARSE_OPT_KEEP_UNKNOWN) {
> + ctx.out[ctx.cpidx++] = ctx.argv[0];
Actually this doesn't work because it may point into the strbuf that
will be invalidated later. Our sole option here is to leak some memory I
fear.
--
·O· Pierre Habouzit
··O madcoder@debian.org
OOO http://www.madism.org
[-- Attachment #2: Type: application/pgp-signature, Size: 197 bytes --]
^ permalink raw reply
* Re: why is git destructive by default? (i suggest it not be!)
From: Björn Steinbrink @ 2008-06-24 8:35 UTC (permalink / raw)
To: David Jeske; +Cc: Jakub Narebski, Avery Pennarun, Nicolas Pitre, git
On 2008.06.24 08:08:13 -0000, David Jeske wrote:
> To re-ask the same question I asked in my last post, using your ascii
> pictures...
>
>
> Let's assume we're here..
>
> .<---.<---.<---A<---X<---Y <--- master
> \
> \--B<---C <--- customer_A_branch <=== HEAD
>
>
> And this person and everyone else moves their head pointers back to master
> without merging:
>
>
> .<---.<---.<---A<---X<---Y <--- master <=== HEAD
> \
> \--B<---C <--- customer_A_branch
>
>
> Now, five years down the road, our tree looks like:
>
>
> .<---A<---X<---Y<---.<--.<--.(3 years of changes)<---ZZZ<--- master <=== HEAD
> \
> \--B<---C <--- customer_A_branch
>
> And someone does:
>
> git-branch -f customer_A_branch ZZZ
>
> To bring us to:
>
> .<---A<---X<---Y<---.<--.(3 years of changes)<---ZZZ<--- master <=== HEAD
> \ \
> \--B<---C \-- customer_A_branch
>
>
> ..at this point, will a GC keep "B<--C", or garbage collect the commits and
> throw them away?
That would throw away the changes in _that_ repository after the reflog
entry has expired. It would not affect any other repo yet, and if that
developer tries to push that new customer_A_branch, it would be refused,
because it is not a fast-forward. And if the repo he's trying to push to
simply doesn't allow any non-fast-forward pushes, then even push -f
won't help him to destroy anything.
Björn
^ permalink raw reply
* Re: [PATCH v6] gitweb: add test suite with Test::WWW::Mechanize::CGI
From: Lea Wiemann @ 2008-06-24 8:37 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Jakub Narebski, git
In-Reply-To: <7vtzfjea64.fsf@gitster.siamese.dyndns.org>
Junio C Hamano wrote:
> We are passing down SHELL_PATH from primary Makefile to t/
> and you should be able to do the same for Perl path...
I see; that'll work, thanks! Will send v8 soon.
> About the Test::WWW:Mechanize::CGI thing, how widely available is it?
Not very, you basically have to install it from CPAN. If it's not
installed, the only message you get from the test is:
ok 1: skipping gitweb tests, Test::WWW::Mechanize::CGI not found
Should optional test dependencies like Test::WWW::Mechnanize::CGI be
documented in INSTALL?
Best,
Lea
^ permalink raw reply
* Re: why is git destructive by default? (i suggest it not be!)
From: David Jeske @ 2008-06-24 8:30 UTC (permalink / raw)
To: Jeff King; +Cc: Avery Pennarun, Nicolas Pitre, git
In-Reply-To: <20080624081601.GA2692@sigill.intra.peff.net>
This is mostly moot since I've understood that it's easy to set git to never
GC. I guess I'm curious about why those GC fields would ever be set to anything
other than never?
-- Jeff King wrote:
> No. Git keeps the reachable DAG. So if the DAG is part of development
> that is merged into one of your long running branches, or if you keep
> around the branch that points to it, it will never go away.
Right, that's what I thought.
I'm not primarily concerned with what developers can do to their local git
repositories. I'm concerned with what the default sync operations can let them
do to the crown-jewels in the 'central organization repositories' which
everyone is periodically pushing to.
I like that deleting a branch in your repo does not cause it to be deleted in
other repos. Presumably in an organization we could prevent the central repo
from ever accepting branch deletes from developers. (without some kind of
authorization)
Does it have the same protection for all operations that can cause DAGs to be
dangling? For example, if they branch -f" and push the branch?
---
Again it's simple enough for me to just set the GC times to "never" on the
server, and I find git pretty pleasing because I'm a
short-attention-span-comitter. On a perforce or cvs repository, I frequently
tar up subtrees between commits, so i don't lose my work -- git is light-years
ahead of this.
Quite a bit of my fear of losing data came from some issues in the git-gui. I'm
trying out git on a windows project, and windows-shells just don't work right,
so I'm using the "Git Gui". It turns out right-clicking on a history entry in
the gui has no checkout option, and the only option it does have which will let
you move the tree to that place is "reset --hard".. since this was the easiest
thing to find in the GUI, I assumed it was the right way to do it, and then all
my more recent changes disappeared. It doesn't seem to have reflog
functionality, so I couldn't find any way to get back all my changes. I ended
up having an old history window that I did another reset --head in back to the
latest change, but I got scared about what git was doing underneath. The docs
clearly explained that it will garbage collect dangling refs, and frankly the
information about how often this happens is buried so deep I had no idea what
the frequency was.
^ permalink raw reply
* Re: why is git destructive by default? (i suggest it not be!)
From: David Jeske @ 2008-06-24 8:30 UTC (permalink / raw)
To: Jeff King; +Cc: Avery Pennarun, Nicolas Pitre, git
In-Reply-To: <20080624081601.GA2692@sigill.intra.peff.net>
This is mostly moot since I've understood that it's easy to set git to never
GC. I guess I'm curious about why those GC fields would ever be set to anything
other than never?
-- Jeff King wrote:
> No. Git keeps the reachable DAG. So if the DAG is part of development
> that is merged into one of your long running branches, or if you keep
> around the branch that points to it, it will never go away.
Right, that's what I thought.
I'm not primarily concerned with what developers can do to their local git
repositories. I'm concerned with what the default sync operations can let them
do to the crown-jewels in the 'central organization repositories' which
everyone is periodically pushing to.
I like that deleting a branch in your repo does not cause it to be deleted in
other repos. Presumably in an organization we could prevent the central repo
from ever accepting branch deletes from developers. (without some kind of
authorization)
Does it have the same protection for all operations that can cause DAGs to be
dangling? For example, if they branch -f" and push the branch?
---
Again it's simple enough for me to just set the GC times to "never" on the
server, and I find git pretty pleasing because I'm a
short-attention-span-comitter. On a perforce or cvs repository, I frequently
tar up subtrees between commits, so i don't lose my work -- git is light-years
ahead of this.
Quite a bit of my fear of losing data came from some issues in the git-gui. I'm
trying out git on a windows project, and windows-shells just don't work right,
so I'm using the "Git Gui". It turns out right-clicking on a history entry in
the gui has no checkout option, and the only option it does have which will let
you move the tree to that place is "reset --hard".. since this was the easiest
thing to find in the GUI, I assumed it was the right way to do it, and then all
my more recent changes disappeared. It doesn't seem to have reflog
functionality, so I couldn't find any way to get back all my changes. I ended
up having an old history window that I did another reset --head in back to the
latest change, but I got scared about what git was doing underneath. The docs
clearly explained that it will garbage collect dangling refs, and frankly the
information about how often this happens is buried so deep I had no idea what
the frequency was.
^ permalink raw reply
* [PATCH 2/7] parse-opt: Export a non NORETURN usage dumper.
From: Pierre Habouzit @ 2008-06-24 9:12 UTC (permalink / raw)
To: git; +Cc: torvalds, gitster, peff, Johannes.Schindelin, Pierre Habouzit
In-Reply-To: <1214298732-6247-2-git-send-email-madcoder@debian.org>
Signed-off-by: Pierre Habouzit <madcoder@debian.org>
---
parse-options.c | 24 +++++++++++++++++-------
parse-options.h | 9 +++++++++
2 files changed, 26 insertions(+), 7 deletions(-)
diff --git a/parse-options.c b/parse-options.c
index 774e647..4e9e675 100644
--- a/parse-options.c
+++ b/parse-options.c
@@ -257,8 +257,8 @@ int parse_options_end(struct parse_opt_ctx_t *ctx)
return ctx->cpidx + ctx->argc;
}
-static NORETURN void usage_with_options_internal(const char * const *,
- const struct option *, int);
+static int usage_with_options_internal(const char * const *,
+ const struct option *, int, int);
int parse_options(int argc, const char **argv, const struct option *options,
const char * const usagestr[], int flags)
@@ -302,7 +302,7 @@ int parse_options(int argc, const char **argv, const struct option *options,
}
if (!strcmp(arg + 2, "help-all"))
- usage_with_options_internal(usagestr, options, 1);
+ usage_with_options_internal(usagestr, options, 1, 1);
if (!strcmp(arg + 2, "help"))
usage_with_options(usagestr, options);
if (parse_long_opt(&ctx, arg + 2, options))
@@ -315,8 +315,8 @@ int parse_options(int argc, const char **argv, const struct option *options,
#define USAGE_OPTS_WIDTH 24
#define USAGE_GAP 2
-void usage_with_options_internal(const char * const *usagestr,
- const struct option *opts, int full)
+int usage_with_options_internal(const char * const *usagestr,
+ const struct option *opts, int full, int do_exit)
{
fprintf(stderr, "usage: %s\n", *usagestr++);
while (*usagestr && **usagestr)
@@ -401,15 +401,25 @@ void usage_with_options_internal(const char * const *usagestr,
}
fputc('\n', stderr);
- exit(129);
+ if (do_exit)
+ exit(129);
+ return PARSE_OPT_HELP;
}
void usage_with_options(const char * const *usagestr,
const struct option *opts)
{
- usage_with_options_internal(usagestr, opts, 0);
+ usage_with_options_internal(usagestr, opts, 0, 1);
+ exit(129); /* make gcc happy */
+}
+
+int parse_options_usage(const char * const *usagestr,
+ const struct option *opts)
+{
+ return usage_with_options_internal(usagestr, opts, 0, 0);
}
+
/*----- some often used options -----*/
#include "cache.h"
diff --git a/parse-options.h b/parse-options.h
index db6c986..424ea46 100644
--- a/parse-options.h
+++ b/parse-options.h
@@ -113,6 +113,12 @@ extern NORETURN void usage_with_options(const char * const *usagestr,
/*----- incremantal advanced APIs -----*/
+enum {
+ PARSE_OPT_HELP = -1,
+ PARSE_OPT_DONE,
+ PARSE_OPT_UNKNOWN,
+};
+
struct parse_opt_ctx_t {
const char **argv;
const char **out;
@@ -121,6 +127,9 @@ struct parse_opt_ctx_t {
int flags;
};
+extern int parse_options_usage(const char * const *usagestr,
+ const struct option *opts);
+
extern void parse_options_start(struct parse_opt_ctx_t *ctx,
int argc, const char **argv, int flags);
--
1.5.6.110.g736c7.dirty
^ permalink raw reply related
* Making parse-opt incremental, reworked series
From: Pierre Habouzit @ 2008-06-24 9:12 UTC (permalink / raw)
To: git; +Cc: torvalds, gitster, peff, Johannes.Schindelin
In-Reply-To: <alpine.LFD.1.10.0806222207220.2926@woody.linux-foundation.org>
Here is a cleaned up series, with Junio's fixes, and some more.
The patch 7 is only a proof of concept, not meant to be merged at all, I
believe the rest is ready enough for now.
I'll try to do what I comment in patch 7/7 (reworking revisions option
parsing to be incremental as well) as soon as I have some time for it.
^ permalink raw reply
* [PATCH 3/7] parse-opt: create parse_options_step.
From: Pierre Habouzit @ 2008-06-24 9:12 UTC (permalink / raw)
To: git; +Cc: torvalds, gitster, peff, Johannes.Schindelin, Pierre Habouzit
In-Reply-To: <1214298732-6247-3-git-send-email-madcoder@debian.org>
For now it's unable to stop at unknown options, this commit merely
reorganize some code around.
Signed-off-by: Pierre Habouzit <madcoder@debian.org>
---
parse-options.c | 89 +++++++++++++++++++++++++++++++-----------------------
parse-options.h | 4 ++
2 files changed, 55 insertions(+), 38 deletions(-)
diff --git a/parse-options.c b/parse-options.c
index 4e9e675..71b3476 100644
--- a/parse-options.c
+++ b/parse-options.c
@@ -250,64 +250,79 @@ void parse_options_start(struct parse_opt_ctx_t *ctx,
ctx->flags = flags;
}
-int parse_options_end(struct parse_opt_ctx_t *ctx)
-{
- memmove(ctx->out + ctx->cpidx, ctx->argv, ctx->argc * sizeof(*ctx->out));
- ctx->out[ctx->cpidx + ctx->argc] = NULL;
- return ctx->cpidx + ctx->argc;
-}
-
static int usage_with_options_internal(const char * const *,
- const struct option *, int, int);
+ const struct option *, int);
-int parse_options(int argc, const char **argv, const struct option *options,
- const char * const usagestr[], int flags)
+int parse_options_step(struct parse_opt_ctx_t *ctx,
+ const struct option *options,
+ const char * const usagestr[])
{
- struct parse_opt_ctx_t ctx;
-
- parse_options_start(&ctx, argc, argv, flags);
- for (; ctx.argc; ctx.argc--, ctx.argv++) {
- const char *arg = ctx.argv[0];
+ for (; ctx->argc; ctx->argc--, ctx->argv++) {
+ const char *arg = ctx->argv[0];
if (*arg != '-' || !arg[1]) {
- if (ctx.flags & PARSE_OPT_STOP_AT_NON_OPTION)
+ if (ctx->flags & PARSE_OPT_STOP_AT_NON_OPTION)
break;
- ctx.out[ctx.cpidx++] = ctx.argv[0];
+ ctx->out[ctx->cpidx++] = ctx->argv[0];
continue;
}
if (arg[1] != '-') {
- ctx.opt = arg + 1;
- if (*ctx.opt == 'h')
- usage_with_options(usagestr, options);
- if (parse_short_opt(&ctx, options) < 0)
+ ctx->opt = arg + 1;
+ if (*ctx->opt == 'h')
+ return parse_options_usage(usagestr, options);
+ if (parse_short_opt(ctx, options) < 0)
usage_with_options(usagestr, options);
- if (ctx.opt)
+ if (ctx->opt)
check_typos(arg + 1, options);
- while (ctx.opt) {
- if (*ctx.opt == 'h')
- usage_with_options(usagestr, options);
- if (parse_short_opt(&ctx, options) < 0)
+ while (ctx->opt) {
+ if (*ctx->opt == 'h')
+ return parse_options_usage(usagestr, options);
+ if (parse_short_opt(ctx, options) < 0)
usage_with_options(usagestr, options);
}
continue;
}
if (!arg[2]) { /* "--" */
- if (!(ctx.flags & PARSE_OPT_KEEP_DASHDASH)) {
- ctx.argc--;
- ctx.argv++;
+ if (!(ctx->flags & PARSE_OPT_KEEP_DASHDASH)) {
+ ctx->argc--;
+ ctx->argv++;
}
break;
}
if (!strcmp(arg + 2, "help-all"))
- usage_with_options_internal(usagestr, options, 1, 1);
+ return usage_with_options_internal(usagestr, options, 1);
if (!strcmp(arg + 2, "help"))
- usage_with_options(usagestr, options);
- if (parse_long_opt(&ctx, arg + 2, options))
+ return parse_options_usage(usagestr, options);
+ if (parse_long_opt(ctx, arg + 2, options))
usage_with_options(usagestr, options);
}
+ return PARSE_OPT_DONE;
+}
+
+int parse_options_end(struct parse_opt_ctx_t *ctx)
+{
+ memmove(ctx->out + ctx->cpidx, ctx->argv, ctx->argc * sizeof(*ctx->out));
+ ctx->out[ctx->cpidx + ctx->argc] = NULL;
+ return ctx->cpidx + ctx->argc;
+}
+
+int parse_options(int argc, const char **argv, const struct option *options,
+ const char * const usagestr[], int flags)
+{
+ struct parse_opt_ctx_t ctx;
+
+ parse_options_start(&ctx, argc, argv, flags);
+ switch (parse_options_step(&ctx, options, usagestr)) {
+ case PARSE_OPT_HELP:
+ exit(129);
+ case PARSE_OPT_DONE:
+ break;
+ default: /* PARSE_OPT_UNKNOWN */
+ abort(); /* unreached yet */
+ }
return parse_options_end(&ctx);
}
@@ -316,7 +331,7 @@ int parse_options(int argc, const char **argv, const struct option *options,
#define USAGE_GAP 2
int usage_with_options_internal(const char * const *usagestr,
- const struct option *opts, int full, int do_exit)
+ const struct option *opts, int full)
{
fprintf(stderr, "usage: %s\n", *usagestr++);
while (*usagestr && **usagestr)
@@ -401,22 +416,20 @@ int usage_with_options_internal(const char * const *usagestr,
}
fputc('\n', stderr);
- if (do_exit)
- exit(129);
return PARSE_OPT_HELP;
}
void usage_with_options(const char * const *usagestr,
const struct option *opts)
{
- usage_with_options_internal(usagestr, opts, 0, 1);
- exit(129); /* make gcc happy */
+ usage_with_options_internal(usagestr, opts, 0);
+ exit(129);
}
int parse_options_usage(const char * const *usagestr,
const struct option *opts)
{
- return usage_with_options_internal(usagestr, opts, 0, 0);
+ return usage_with_options_internal(usagestr, opts, 0);
}
diff --git a/parse-options.h b/parse-options.h
index 424ea46..9da5e8c 100644
--- a/parse-options.h
+++ b/parse-options.h
@@ -133,6 +133,10 @@ extern int parse_options_usage(const char * const *usagestr,
extern void parse_options_start(struct parse_opt_ctx_t *ctx,
int argc, const char **argv, int flags);
+extern int parse_options_step(struct parse_opt_ctx_t *ctx,
+ const struct option *options,
+ const char * const usagestr[]);
+
extern int parse_options_end(struct parse_opt_ctx_t *ctx);
--
1.5.6.110.g736c7.dirty
^ permalink raw reply related
* [PATCH 4/7] parse-opt: do not pring errors on unknown options, return -2 intead.
From: Pierre Habouzit @ 2008-06-24 9:12 UTC (permalink / raw)
To: git; +Cc: torvalds, gitster, peff, Johannes.Schindelin, Pierre Habouzit
In-Reply-To: <1214298732-6247-4-git-send-email-madcoder@debian.org>
This way we can catch "unknown" options more easily.
Signed-off-by: Pierre Habouzit <madcoder@debian.org>
---
parse-options.c | 43 ++++++++++++++++++++++++++++++-------------
1 files changed, 30 insertions(+), 13 deletions(-)
diff --git a/parse-options.c b/parse-options.c
index 71b3476..90935f3 100644
--- a/parse-options.c
+++ b/parse-options.c
@@ -94,14 +94,14 @@ static int get_value(struct parse_opt_ctx_t *p,
case OPTION_CALLBACK:
if (unset)
- return (*opt->callback)(opt, NULL, 1);
+ return (*opt->callback)(opt, NULL, 1) ? (-1) : 0;
if (opt->flags & PARSE_OPT_NOARG)
- return (*opt->callback)(opt, NULL, 0);
+ return (*opt->callback)(opt, NULL, 0) ? (-1) : 0;
if (opt->flags & PARSE_OPT_OPTARG && !p->opt)
- return (*opt->callback)(opt, NULL, 0);
+ return (*opt->callback)(opt, NULL, 0) ? (-1) : 0;
if (!arg)
return opterror(opt, "requires a value", flags);
- return (*opt->callback)(opt, get_arg(p), 0);
+ return (*opt->callback)(opt, get_arg(p), 0) ? (-1) : 0;
case OPTION_INTEGER:
if (unset) {
@@ -132,7 +132,7 @@ static int parse_short_opt(struct parse_opt_ctx_t *p, const struct option *optio
return get_value(p, options, OPT_SHORT);
}
}
- return error("unknown switch `%c'", *p->opt);
+ return -2;
}
static int parse_long_opt(struct parse_opt_ctx_t *p, const char *arg,
@@ -217,7 +217,7 @@ is_abbreviated:
abbrev_option->long_name);
if (abbrev_option)
return get_value(p, abbrev_option, abbrev_flags);
- return error("unknown option `%s'", arg);
+ return -2;
}
void check_typos(const char *arg, const struct option *options)
@@ -271,15 +271,23 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,
ctx->opt = arg + 1;
if (*ctx->opt == 'h')
return parse_options_usage(usagestr, options);
- if (parse_short_opt(ctx, options) < 0)
- usage_with_options(usagestr, options);
+ switch (parse_short_opt(ctx, options)) {
+ case -1:
+ return parse_options_usage(usagestr, options);
+ case -2:
+ return PARSE_OPT_UNKNOWN;
+ }
if (ctx->opt)
check_typos(arg + 1, options);
while (ctx->opt) {
if (*ctx->opt == 'h')
return parse_options_usage(usagestr, options);
- if (parse_short_opt(ctx, options) < 0)
- usage_with_options(usagestr, options);
+ switch (parse_short_opt(ctx, options)) {
+ case -1:
+ return parse_options_usage(usagestr, options);
+ case -2:
+ return PARSE_OPT_UNKNOWN;
+ }
}
continue;
}
@@ -296,8 +304,12 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,
return usage_with_options_internal(usagestr, options, 1);
if (!strcmp(arg + 2, "help"))
return parse_options_usage(usagestr, options);
- if (parse_long_opt(ctx, arg + 2, options))
- usage_with_options(usagestr, options);
+ switch (parse_long_opt(ctx, arg + 2, options)) {
+ case -1:
+ return parse_options_usage(usagestr, options);
+ case -2:
+ return PARSE_OPT_UNKNOWN;
+ }
}
return PARSE_OPT_DONE;
}
@@ -321,7 +333,12 @@ int parse_options(int argc, const char **argv, const struct option *options,
case PARSE_OPT_DONE:
break;
default: /* PARSE_OPT_UNKNOWN */
- abort(); /* unreached yet */
+ if (ctx.argv[0][1] == '-') {
+ error("unknown option `%s'", ctx.argv[0] + 2);
+ } else {
+ error("unknown switch `%c'", *ctx.opt);
+ }
+ usage_with_options(usagestr, options);
}
return parse_options_end(&ctx);
--
1.5.6.110.g736c7.dirty
^ 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