This adds --date-order to rev-list; it is similar to topo order
in the sense that no parent comes before all of its children,
but otherwise things are still ordered in the commit timestamp
order.
The same flag is also added to show-branch.
Signed-off-by: Junio C Hamano <junkio@cox.net>
When we prettyprint commit log messages, merge parent names were
often very long and there was no way to abbreviate it.
This changes them to be abbreviated by default, and non-default
abbreviations can be specified with --no-abbrev or --abbrev=<n>
options.
Note that this affects only the prettyprinted parent names. The
output from --show-parents is meant for machine consumption and
is not affected by this flag.
Showing the same parent more than once for a commit does not
make much sense downstream, so stop it.
This can happen with an incorrectly made merge commit that
merges the same parent twice, but can happen in an otherwise
sane development history while squishing the history by taking
into account only commits that touch specified paths.
For example,
$ git rev-list --max-count=1 --parents addafaf -- rev-list.c
would have to show this commit ancestry graph:
.---o---.
/ \
.---*---o---.
/ 93b74bc \
---*---o---o-----o---o-----o addafafd8f6b34 \ /
.---o---o---.
\ /
.---*---.
3815f42
where 5 independent development tracks, only two of which have
changes in the specified paths since they forked. The last
change for the other three development tracks was done by the
same commit before they forked, and we were showing that three
times.
Signed-off-by: Junio C Hamano <junkio@cox.net>
This builds on top of the previous one.
Traditionally, head(1) and tail(1) allow their line limits to be
parsed this way.
Signed-off-by: Eric Wong <normalperson@yhbt.net>
Signed-off-by: Junio C Hamano <junkio@cox.net>
Both -n<n> and -n <n> are supported. POSIX versions of head(1) and
tail(1) allow their line limits to be parsed this way. I find
--max-count to be a commonly used option, and also similar in spirit to
head/tail, so I decided to make life easier on my worn out (and lazy :)
fingers with this patch.
Signed-off-by: Eric Wong <normalperson@yhbt.net>
Signed-off-by: Junio C Hamano <junkio@cox.net>
The one thing I've considered doing (I really should) is to add a "stop
when you don't find the file" option to "git-rev-list". This patch does
some of the work towards that: it removes the "parent" thing when the
file disappears, so a "git annotate" could do do something like
git-rev-list --remove-empty --parents HEAD -- "$filename"
and it would get a good graph that stops when the filename disappears
(it's not perfect though: it won't remove all the unintersting commits).
It also simplifies the logic of finding tree differences a bit, at the
cost of making it a tad less efficient.
The old logic was two-phase: it would first simplify _only_ merges tree as
it traversed the tree, and then simplify the linear parts of the remainder
independently. That was pretty optimal from an efficiency standpoint
because it avoids doing any comparisons that we can see are unnecessary,
but it made it much harder to understand than it really needed to be.
The new logic is a lot more straightforward, and compares the trees as it
traverses the graph (ie everything is a single phase). That makes it much
easier to stop graph traversal at any point where a file disappears.
As an example, let's say that you have a git repository that has had a
file called "A" some time in the past. That file gets renamed to B, and
then gets renamed back again to A. The old "git-rev-list" would show two
commits: the commit that renames B to A (because it changes A) _and_ as
its parent the commit that renames A to B (because it changes A).
With the new --remove-empty flag, git-rev-list will show just the commit
that renames B to A as the "root" commit, and stop traversal there
(because that's what you want for "annotate" - you want to stop there, and
for every "root" commit you then separately see if it really is a new
file, or if the paths history disappeared because it was renamed from some
other file).
With this patch, you should be able to basically do a "poor mans 'git
annotate'" with a fairly simple loop:
push("HEAD", "$filename")
while (revision,filename = pop()) {
for each i in $(git-rev-list --parents --remove-empty $revision -- "$filename")
pseudo-parents($i) = git-rev-list parents for that line
if (pseudo-parents($i) is non-empty) {
show diff of $i against pseudo-parents
continue
}
/* See if the _real_ parents of $i had a rename */
parent($i) = real-parent($i)
if (find-rename in $parent($i)->$i)
push $parent($i), "old-name"
}
which should be doable in perl or something (doing stacks in shell is just
too painful to be worth it, so I'm not going to do this).
Anybody want to try?
Linus
If you pass it a filename without the "--" marker to separate it from
revision information and flags, we now require that the file in question
actually exists. This makes mis-typed revision information not be silently
just considered a strange filename.
With the "--" marker, you can continue to pass in filenames that do not
actually exists - useful for querying what happened to a file that you
no longer have in the repository.
[ All scripts should use the "--" format regardless, to make things
unambiguous. So this change should not affect any existing tools ]
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
Earlier, "rev-list --objects <sha1>" for an object chain that
does not have any commit failed with a usage message. This
fixes "send-pack remote $tag" where tag points at a non-commit
(e.g. a blob).
Signed-off-by: Junio C Hamano <junkio@cox.net>
It was surprisingly easy to do.
git bisect start <pathspec>
followed by all the normal "git bisect good/bad" stuff.
Almost totally untested, and I guarantee that if your pathnames have
spaces in them (or your GIT_DIR has spaces in it) this won't work. I don't
know how to fix that, my shell programming isn't good enough.
This involves small changes to make "git-rev-list --bisect" work in the
presense of a pathspec limiter, and then truly trivial (and that's the
broken part) changes to make "git bisect" save away and use the pathspec.
I tried one bisection, and a "git bisect visualize", and it all looked
correct. But hey, don't be surprised if it has problems.
Linus
Signed-off-by: Junio C Hamano <junkio@cox.net>
When a path designation is given, max-count counts the number
of commits therein (intersection), not globally.
This avoids the case where in case path has been inactive
for the last N commits, --max-count=N and path designation
at git-rev-list is given, would give no commits.
Signed-off-by: Luben Tuikov <ltuikov@yahoo.com>
Signed-off-by: Junio C Hamano <junkio@cox.net>
What happens is that the new logic decides that if it can't look up a
commit reference (ie "get_commit_reference()" returns NULL), the thing
must be a pathname.
Fair enough.
But wrong.
The thing is, it may be a perfectly fine ref that _isn't_ a commit. In
git, you have a tag that points to your PGP key, and in the kernel, I have
a tag that points to a tree (and a direct ref that points to that tree
too, for that matter).
So the rule is (as for all the other programs that mix revs and pathnames)
not that we only accept commit references, but _any_ valid object ref.
If the object then isn't a commit ref, git-rev-list will either ignore it,
or add it to the list of non-commit objects (if using "--objects").
The solution is to move the "get_sha1()" out of get_commit_reference(),
and into the callers. In fact, we already _have_ the SHA1 in the case of
the handle_all() loop, since for_each_ref() will have done it for us, so
this is the correct thing to do anyway.
This patch (on top of the original one) does exactly that.
Signed-off-by: Junio C Hamano <junkio@cox.net>
This actually does three things:
- make "--dense" the default for git-rev-list. Since dense is a no-op if
no filenames are given, this doesn't actually change any historical
behaviour, but it's logically the right default (if we want to prune on
filenames, do it fully. The sparse "merge-only" thing may be useful,
but it's not what you'd normally expect)
- make "git-rev-parse" show the default revision control before it shows
any pathnames.
This was a real bug, but nobody would ever have noticed, because
the default thing tends to only make sense for git-rev-list, and
git-rev-list didn't use to take pathnames.
- it changes "git-rev-list" to match the other commands that take a mix
of revisions and filenames - it no longer requires the "--" before
filenames (although you still need to do it if a filename could be
confused with a revision name, eg "gitk" in the git archive)
This all just makes for much more pleasant and obvous usage. Just doing a
gitk t/
does the obvious thing: it will show the history as it concerns the "t/"
subdirectory.
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
Right now --dense will _always_ show the root commit. I didn't do the
logic that does the diff against an empty tree. I was lazy.
This patch does that. The first round was incorrect but
this patch is even slightly tested, and might do a better job.
Signed-off-by: Junio C Hamano <junkio@cox.net>
This is what the recent git-rev-list changes have all been gearing up for.
When we use a path filter to git-rev-list, the new "--dense" flag asks
git-rev-list to compress the history so that it _only_ contains commits
that change files in the path filter. It also rewrites the parent
information so that tools like "gitk" will see the result as a dense
history tree.
For example, on the current kernel archive:
[torvalds@g5 linux]$ git-rev-list HEAD | wc -l
9904
[torvalds@g5 linux]$ git-rev-list HEAD -- kernel | wc -l
5442
[torvalds@g5 linux]$ git-rev-list --dense HEAD -- kernel | wc -l
356
which shows that while we have almost ten thousand commits, we can prune
down the work to slightly more than half by only following the merges
that are interesting. But further, we can then compress the history to
just 356 entries that actually make changes to the kernel subdirectory.
To see this in action, try something like
gitk --dense -- gitk
to see just the history that affects gitk. Or, to show that true
parallel development still remains parallel, do
gitk --dense -- daemon.c
which shows some parallel commits in the current git tree.
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
This is the first cut at a git-rev-list that knows to ignore commits that
don't change a certain file (or set of files).
NOTE! For now it only prunes _merge_ commits, and follows the parent where
there are no differences in the set of files specified. In the long run,
I'd like to make it re-write the straight-line history too, but for now
the merge simplification is much more fundamentally important (the
rewriting of straight-line history is largely a separate simplification
phase, but the merge simplification needs to happen early if we want to
optimize away unnecessary commit parsing).
If all parents of a merge change some of the files, the merge is left as
is, so the end result is in no way guaranteed to be a linear history, but
it will often be a lot /more/ linear than the full tree, since it prunes
out parents that didn't matter for that set of files.
As an example from the current kernel:
[torvalds@g5 linux]$ git-rev-list HEAD | wc -l
9885
[torvalds@g5 linux]$ git-rev-list HEAD -- Makefile | wc -l
4084
[torvalds@g5 linux]$ git-rev-list HEAD -- drivers/usb | wc -l
5206
and you can also use 'gitk' to more visually see the pruning of the
history tree, with something like
gitk -- drivers/usb
showing a simplified history that tries to follow the first parent in a
merge that is the parent that fully defines drivers/usb/.
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
I took a look at webgit, and it looks like at least for the "projects"
page, the most common operation ends up being basically
git-rev-list --header --parents --max-count=1 HEAD
Now, the thing is, the way "git-rev-list" works, it always keeps on
popping the parents and parsing them in order to build the list of
parents, and it turns out that even though we just want a single commit,
git-rev-list will invariably look up _three_ generations of commits.
It will parse:
- the commit we want (it obviously needs this)
- it's parent(s) as part of the "pop_most_recent_commit()" logic
- it will then pop one of the parents before it notices that it doesn't
need any more
- and as part of popping the parent, it will parse the grandparent (again
due to "pop_most_recent_commit()".
Now, I've strace'd it, and it really is pretty efficient on the whole, but
if things aren't nicely cached, and with long-latency IO, doing those two
extra objects (at a minimum - if the parent is a merge it will be more) is
just wasted time, and potentially a lot of it.
So here's a quick special-case for the trivial case of "just one commit,
and no date-limits or other special rules".
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
Cloning from a repository with more than 256 refs (heads and tags
included) will choke, because upload-pack has a built-in limit of
feeding not more than MAX_NEEDS (currently 256) heads to underlying
git-rev-list. This is a problem when cloning a repository with many
tags, like http://www.linux-mips.org/pub/scm/linux.git, which has 290+
tags.
This commit introduces a new flag, --all, to git-rev-list, to include
all refs in the repository. Updated upload-pack detects requests that
ask more than MAX_NEEDS refs, and sends everything back instead.
We may probably want to tweak the definitions of MAX_NEEDS and
MAX_HAS, but that is a separate topic.
Signed-off-by: Junio C Hamano <junkio@cox.net>
A carefully crafted pathname can be used to disrupt downstream git-pack-objects
that uses 'git-rev-list --objects' output. Prevent this.
Signed-off-by: Junio C Hamano <junkio@cox.net>
The trick is to consider the time-based filtering a limiter, the same way
we do for release ranges.
That means that the time-based filtering runs _before_ the topological
sorting, which makes it meaningful again. It also simplifies the code
logic.
This makes "gitk" useful with time ranges.
[ Second version: --merge-order now unaffected by the re-org ]
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
There were two bugs in there:
- if the range didn't end up working, we restored the '.' character in
the wrong place.
- an empty end-of-range should be interpreted as HEAD.
See rev-parse.c for the reference implementation of this.
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
The object parsing code builds a generic "this object references that
object" because doing a full connectivity check for fsck requires it.
However, nothing else really needs it, and it's quite expensive for
git-rev-list that can have tons of objects in flight.
So, exactly like the commit buffer save thing, add a global flag to
disable it, and use it in git-rev-list.
Before:
$ /usr/bin/time git-rev-list --objects v2.6.12..HEAD | wc -l
12.28user 0.29system 0:12.57elapsed 99%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (0major+26718minor)pagefaults 0swaps
59124
After this change:
$ /usr/bin/time git-rev-list --objects v2.6.12..HEAD | wc -l
10.33user 0.18system 0:10.54elapsed 99%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (0major+18509minor)pagefaults 0swaps
59124
and note how the number of pages touched by git-rev-list for this
particular object list has shrunk from 26,718 (104 MB) to 18,509 (72 MB).
Calculating the total object difference between two git revisions is still
clearly the most expensive git operation (both in memory and CPU time),
but it's now less than 40% of what it used to be.
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
This avoids keeping tree entries around, and free's them as it traverses
the list. This avoids building up a huge memory footprint just for these
small but very common allocations.
Before:
$ /usr/bin/time git-rev-list --objects v2.6.12..HEAD | wc -l
11.65user 0.38system 0:12.65elapsed 95%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (0major+42934minor)pagefaults 0swaps
59124
After:
$ /usr/bin/time git-rev-list --objects v2.6.12..HEAD | wc -l
12.28user 0.29system 0:12.57elapsed 99%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (0major+26718minor)pagefaults 0swaps
59124
Note how the minor fault numbers - which ends up being how many pages we
needed to map - go down from 42934 (167 MB) to 26718 (104 MB). That is:
Before:
42934 minor pagefaults
After:
26718 minor pagefaults
This is all in _addition_ to the previous fixes. It used to be
~48,000 pagefaults.
That's still a honking big memory footprint, but it's about half of what
it was just a day or two ago (and this is the object list for a pretty big
update - almost 60,000 objects. Smaller updates need less memory).
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
The logic to calculate the full object list used to be very inter-twined
with the logic that looked up the commits.
For no good reason - it's actually a lot simpler to just do that logic
as a separate pass.
This improves performance a bit, and uses slightly less memory in my
tests, but more importantly it makes the code simpler to work with and
follow what it does.
The performance win is less than I had hoped for, but I get:
Before:
[torvalds@g5 linux]$ /usr/bin/time git-rev-list --objects v2.6.12..HEAD | wc -l
13.64user 0.42system 0:14.13elapsed 99%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (0major+47947minor)pagefaults 0swaps
58945
After:
[torvalds@g5 linux]$ /usr/bin/time git-rev-list --objects v2.6.12..HEAD | wc -l
11.80user 0.36system 0:12.16elapsed 99%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (0major+42684minor)pagefaults 0swaps
58945
ie it improved by 2 seconds, and took a 5000+ fewer pages (hey, that's
20MB out of 174MB to go). And got the same number of objects (in theory,
the more expensive one might find some more shared objects to avoid. In
practice it obviously doesn't).
I know how to make it use _lots_ less memory, which will probably speed it
up. But that's for another time, and I'd prefer to see this go in first.
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
As pointed out on the list, git-rev-list can use a lot of memory.
One low-hanging fruit is to free the commit buffer for commits that we
parse. By default, parse_commit() will save away the buffer, since a lot
of cases do want it, and re-reading it continually would be unnecessary.
However, in many cases the buffer isn't actually necessary and saving it
just wastes memory.
We could just free the buffer ourselves, but especially in git-rev-list,
we actually end up using the helper functions that automatically add
parent commits to the commit lists, so we don't actually control the
commit parsing directly.
Instead, just make this behaviour of "parse_commit()" a global flag.
Maybe this is a bit tasteless, but it's very simple, and it makes a
noticable difference in memory usage.
Before the change:
[torvalds@g5 linux]$ /usr/bin/time git-rev-list v2.6.12..HEAD > /dev/null
0.26user 0.02system 0:00.28elapsed 99%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (0major+3714minor)pagefaults 0swaps
after the change:
[torvalds@g5 linux]$ /usr/bin/time git-rev-list v2.6.12..HEAD > /dev/null
0.26user 0.00system 0:00.27elapsed 100%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (0major+2433minor)pagefaults 0swaps
note how the minor faults have decreased from 3714 pages to 2433 pages.
That's all due to the fewer anonymous pages allocated to hold the comment
buffers and their metadata.
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
Recent changes in git have broken cg-log. git-rev-list no longer
prints "commit" in front of commit hashes. It turn out a local
"prefix" variable in main() shadows a file-scoped "prefix" variable.
The patch removed the local "prefix" variable since its value is never
used (in the intended way, that is). The call to
setup_git_directory() is kept since it has useful side effects.
The file-scoped "prefix" variable is renamed to "commit_prefix" just
in case someone reintroduces "prefix" to hold the return value of
setup_git_directory().
Signed-off-by: Pavel Roskin <proski@gnu.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
This trivial patch makes "git-rev-list" able to handle not being in
the top-level directory. This magically also makes "git-whatchanged"
do the right thing.
Trivial scripting fix to make sure that "git log" also works.
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
When following tags, check for parse_object() success and error out
properly instead of segfaulting.
Signed-off-by: Sergey Vlasov <vsu@altlinux.ru>
Signed-off-by: Junio C Hamano <junkio@cox.net>
This introduces --pretty=oneline to git-rev-tree and
git-rev-list commands to show only the first line of the commit
message, without frills.
Signed-off-by: Junio C Hamano <junkio@cox.net>
As requested by Junio (who suggested --single-parents-only, but this
could forget a no-parent root).
Also, adds a few missing options to the usage string.
Signed-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Signed-off-by: Junio C Hamano <junkio@cox.net>
The King Penguin says:
Now, for extra bonus points, maybe you should make "git-rev-list" also
understand the "rev..rev" format (which you can't do with just the
get_sha1() interface, since it expands into more).
The faithful servant makes it so.
Signed-off-by: Junio C Hamano <junkio@cox.net>
Support for completely OpenSSL-less builds. FSF considers distributing GPL
binaries with OpenSSL linked in as a legal problem so this is trouble
e.g. for Debian, or some people might not want to install OpenSSL
anyway. If you
make NO_OPENSSL=1
you get completely OpenSSL-less build, disabling --merge-order and using
Mozilla's SHA1 implementation.
Ported from Cogito.
Signed-off-by: Petr Baudis <pasky@ucw.cz>
Signed-off-by: Junio C Hamano <junkio@cox.net>
This corner-case was triggered by a kernel commit that was not in date
order, due to a misconfigured time zone that made the commit appear three
hours older than it was.
That caused git-rev-list to traverse the commit tree in a non-obvious
order, and made it parse several of the _parents_ of the misplaced commit
before it actually parsed the commit itself. That's fine, but it meant
that the grandparents of the commit didn't get marked uninteresting,
because they had been reached through an "interesting" branch.
The reason was that "mark_parents_uninteresting()" (which is supposed to
mark all existing parents as being uninteresting - duh) didn't actually
traverse more than one level down the parent chain.
NORMALLY this is fine, since with the date-based traversal order,
grandparents won't ever even have been looked at before their parents (so
traversing the chain down isn't needed, because the next time around when
we pick out the parent we'll mark _its_ parents uninteresting), but since
we'd gotten out of order, we'd already seen the parent and thus never got
around to mark the grandparents.
Anyway, the fix is simple. Just traverse parent chains recursively.
Normally the chain won't even exist (since the parent hasn't been parsed
yet), so this is not actually going to trigger except in this strange
corner-case.
Add a comment to the simple one-liner, since this was a bit subtle, and I
had to really think things through to understand how it could happen.
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
We'll mark all the trees at the edges (as deep as we had to go to
realize that we have all the commits needed) as uninteresting.
Otherwise we'll occasionally list a lot of objects that were actually
available at the edge in a commit that we just never ended up parsing
because we could determine early that we had all relevant commits.
NOTE! The object listing is still just a _heuristic_. It's guaranteed
to list a superset of the actual new objects, but there might be the
occasional old object in the list, just because the commit that
referenced it was much further back in the history.
For example, let's say that a recent commit is a revert of part of the
tree to much older state: since we didn't walk _that_ far back in the
commit history tree to list the commits necessary, git-rev-tree will
never have marked the old objects uninteresting, and we'll end up
listing them as "new".
That's ok.
When we allow a tag object in place of a commit object, we only
dereferenced the given tag once, which causes a tag that points at a tag
that points at a commit to be rejected. Instead, dereference tag
repeatedly until we get a non-tag.
This patch makes change to two functions:
- commit.c::lookup_commit_reference() is used by merge-base,
rev-tree and rev-parse to convert user supplied SHA1 to that of
a commit.
- rev-list uses its own get_commit_reference() to do the same.
Dereferencing tags this way helps both of these uses.
Signed-off-by: Junio C Hamano <junkio@cox.net>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
This change ensures that git-rev-list --merge-order produces the same result
irrespective of what position the --merge-order argument appears in the argument
list.
Signed-off-by: Jon Seymour <jon.seymour@gmail.com>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
That's what we should have done in the first place, since it not only
avoids another unnecessary flag, it also protects the commits from
showing up as duplicates later when they show up as parents of another
commit (in the pop_most_recent_commit() path).
This will hopefully also fix --topo-sort.
This patch implements a small tidy up of rev-list.c to reduce
(but not eliminate) the amount of ugliness associated
with the merge_order flag.
Signed-off-by: Jon Seymour <jon.seymour@gmail.com>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Not only is it unnecessary, it incorrectly allows extraneous characters
at the end of the argument.
Junio noticed the --merge-order thing, and Jon points out that if we fix
that one, we should fix --show-breaks too.