* [SCRIPT] cg-rpush & locking
From: Tony Lindgren @ 2005-05-31 19:00 UTC (permalink / raw)
To: git; +Cc: Matthias Urlichs
[-- Attachment #1: Type: text/plain, Size: 859 bytes --]
Hello all,
Attached is a little script we're using for pushing changes to the
linux-omap tree. I modified it from an earlier script done by
Matthias Urlichs.
It uses rsync over ssh and should work with rsync write access too.
In order to remove lock files using rsync, I've added
a .git/locks subdirectory that contains the lock file.
The reason for this is that it allows replacing the lock directory
with an empty directory using rsync. This removes the lock file
after the remote update is done.
Currently the local lock has some issues with chained pushes...
If somebody is pushing from a remote repo to the local repo while
the local repo is being pushed to some other remote repo, the lock
may get removed.
Of course the lock does not protect from local changes either.
Anybody have any better ideas for locking that also works with
rsync?
Tony
[-- Attachment #2: cg-rpush --]
[-- Type: text/plain, Size: 3901 bytes --]
#!/bin/sh
#
# Pushes changes from the local git repo to remote repo.
#
# Copyright (C) 2005 Tony Lindgren <tony@atomide.com>
#
# Some parts based on an earlier push script,
# Copyright (C) 2005 Matthias Urlichs.
#
# Takes the remote repo's name or url as parameter.
#
# Most likely the remote repo is not rsync writable, but
# you can use rsync over ssh for the push.
#
# When using rsync over ssh, you must use the real repo
# path on the server and an ssh key to avoid typing in
# the password multiple times. For example:
#
# $ export RSYNC_FLAGS="-z --progress"
# $ export RSYNC_RSH="ssh -i /home/user/.ssh/my-git-key"
# $ cg-rpush some.machine:/home/git/repo
#
. ${COGITO_LIB}cg-Xlib
name=$1
BRANCHES=".git/branches"
HEADS=".git/refs/heads"
OBJECTS=".git/objects"
LOCKS=".git/locks"
REMOTE_LOCK="write_lock"
TMP="/tmp"
uri=""
function usage () {
echo "Usage: [RSYNC_FLAGS=\"-e ssh\"] $0 some.machine:/home/git/repo"
exit 1
}
function die () {
if [ -f $LOCKS/$REMOTE_LOCK ]; then
rm -f $LOCKS/$REMOTE_LOCK
fi
echo cg-rpush: $@ >&2
exit 1
}
function clean_locks () {
rm -f $LOCKS/$REMOTE_LOCK
rsync $RSYNC_FLAGS -r --delete $LOCKS/ $uri/$LOCKS
}
function validate_input () {
[ "$name" ] || usage
if [ ! -d ".git" ]; then
die "Could not find local .git directory"
fi
uri=$(cat $BRANCHES/$name 2>/dev/null)
[ "$uri" ] || uri=$name
echo $uri
}
#
# We must use a lock directory to allow removing the remote lock
# files with rsync by copying over it with an empty directory.
# Creating the remote lock file should be safe. However, please note
# that we must also be careful not to remove local .git/locks/write_lock
# in case somebody is pushing to our local repo from a remote machine.
# Currently the local lock file creation can conflict with a lock
# file creation from a remote machine to our local machine.
#
function lock_files () {
echo "Attempting to to create a write lock on remote..."
if [ ! -d $LOCKS ]; then
mkdir $LOCKS;
fi
if [ -f $LOCKS/$REMOTE_LOCK ]; then
echo "Local write_lock already exists: $LOCKS/$REMOTE_LOCK"
exit 1
fi
lock_stamp="$USER@$HOSTNAME $(date)"
echo $lock_stamp > $LOCKS/$REMOTE_LOCK
rsync $RSYNC_FLAGS -r --ignore-existing $LOCKS/ $uri/$LOCKS
# Check what the remote .git/locks/write_lock has
tmpfile=$TMP/remote_lock_$RANDOM
rsync $RSYNC_FLAGS "$uri/$LOCKS/$REMOTE_LOCK" $tmpfile
remote_stamp=$(cat $tmpfile)
rm -f $tmpfile
if [ "$remote_stamp" != "$lock_stamp" ]; then
die "Remote locked by $remote_stamp, please try again later"
fi
}
function check_remote_version () {
echo "Getting remote version..."
tmpfile=$TMP/remote_head_$RANDOM
rsync $RSYNC_FLAGS -Lr "$uri/$HEADS/master" $tmpfile
remote_head=$(cat $tmpfile)
rm -f $tmpfile
if [ -z "$remote_head" ]; then
clean_locks
die "Remote repository does not have $uri/$HEADS/master"
fi
echo "Remote head is at: $remote_head"
if [ "$(git-cat-file -t "$remote_head" 2>/dev/null)" != "commit" ]; then
clean_locks
die "Remote is ahead, please do a pull first"
fi
}
function push_git_objects () {
echo "Pushing .git/objects..."
rsync $RSYNC_FLAGS --ignore-existing --whole-file -v -r \
"$OBJECTS/" "$uri/$OBJECTS/"
}
function update_remote_head () {
local_head=$(cat $HEADS/master)
echo "Updating remote head to: $local_head"
rsync $RSYNC_FLAGS -Lr $HEADS/master "$uri/$HEADS/master"
}
function print_note () {
echo "Remote updated successfully"
echo "NOTE: Not updating checked out remote files in case they"
echo "have been edited locally on the remote machine."
echo "To sync checked out files on remote, you can run cg-cancel"
echo "on remote machine. You can check for uncommitted changes"
echo "on remote with cg-diff first, which should only show"
echo "changes done in this push."
}
#
# Main program
#
uri=$(validate_input)
lock_files
check_remote_version
push_git_objects
update_remote_head
clean_locks
print_note
exit
^ permalink raw reply
* Re: cg-update with local uncommitted changes
From: Dan Holmsand @ 2005-05-31 19:11 UTC (permalink / raw)
To: git; +Cc: Petr Baudis
In-Reply-To: <20050531155825.GA7013@pasky.ji.cz>
Petr Baudis wrote:
> Dear diary, on Tue, May 31, 2005 at 12:31:28AM CEST, I got a letter
> where Dan Holmsand <holmsand@gmail.com> told me that...
>>This patch would make cg-merge and cg-admin-uncommit refuse to do
>>anything if there are conflicting uncommitted changes. Note: this only
>>applies to fast-forward merging, as cg-merge otherwise bails out if
>>there are *any* uncommitted changes (which is perhaps going to far).
>
>
> Well, non-fast-forward cg-merge will do cg-commit and it would blend the
> unrelated previously uncommitted changes into that, which is not what
> you want.
cg-merge should obviously only commit the files touched by the
"slow-forward" merge (note: this is not a big deal for me, I have no
problem with cogito saying "no" once too often. It's when it tries and
fails that trouble starts).
>>[PATCH] Make tree_timewarp safe, by refusing to handle conflicts.
> I don't really think this makes any sense. What do you then do when you
> want to do some merging of the local uncommitted changes and upstream
> update?
I've never really wanted to do that. I commit, then merge.
> How have you been bitten, and how could we destroy the local changes?
> You get rejects and patch will be pretty vocal about it, so then you
> just go and resolve them. The correct direction is to make it do a
> three-way merge, not make it do no merge at all.
Huh? When I get to the patch rejects, the merge will have happened and
the local changes pretty much be gone forever. And it's really easy to
miss the .rej files altogether.
I'd much prefer not having to worry before doing cg-merge/update, then
to have to salvage old stuff from .rej files. And three-way merges don't
really solve this; they may work more often, but when they fail, data is
potentially lost. Why take the chance?
/dan
^ permalink raw reply
* Re: [PATCH] Make git-update-cache --force-remove regular
From: Junio C Hamano @ 2005-05-31 19:52 UTC (permalink / raw)
To: Petr Baudis; +Cc: torvalds, git
In-Reply-To: <20050531165243.GD7013@pasky.ji.cz>
>>>>> "PB" == Petr Baudis <pasky@ucw.cz> writes:
PB> Make the --force-remove flag behave same as --add, --remove and
PB> --replace. This means I can do
PB> git-update-cache --force-remove -- file1.c file2.c
PB> which is probably saner and also makes it easier to use in cg-rm.
I am ambivalent about this. Although I like the semantic
clean-up your proposed change makes, at the same time:
$ git-update-cache --force-remove one --add two
used to remove "one" and add "two", which has to be now written
as two separate calls, which is a slight a performance hit of
having to read the 1.6MB cache twice. Maybe it does not matter,
or the new usage's convenience outweighs it; I cannot tell
offhand.
^ permalink raw reply
* Re: [PATCH] allow pathspec to end with a slash
From: Junio C Hamano @ 2005-05-31 20:08 UTC (permalink / raw)
To: Linus Torvalds; +Cc: Git Mailing List
In-Reply-To: <Pine.LNX.4.58.0505310827330.1876@ppc970.osdl.org>
Do you want the same for git-ls-tree as well? That is, when
you have drivers/char as a blob in the tree, should
git-ls-tree $tree drivers/char/
show nothing?
^ permalink raw reply
* Re: [PATCH] Make git-update-cache --force-remove regular
From: Petr Baudis @ 2005-05-31 20:41 UTC (permalink / raw)
To: Junio C Hamano; +Cc: torvalds, git
In-Reply-To: <7vpsv7xjf2.fsf@assigned-by-dhcp.cox.net>
Dear diary, on Tue, May 31, 2005 at 09:52:17PM CEST, I got a letter
where Junio C Hamano <junkio@cox.net> told me that...
> >>>>> "PB" == Petr Baudis <pasky@ucw.cz> writes:
>
> PB> Make the --force-remove flag behave same as --add, --remove and
> PB> --replace. This means I can do
>
> PB> git-update-cache --force-remove -- file1.c file2.c
>
> PB> which is probably saner and also makes it easier to use in cg-rm.
>
> I am ambivalent about this. Although I like the semantic
> clean-up your proposed change makes, at the same time:
>
> $ git-update-cache --force-remove one --add two
>
> used to remove "one" and add "two", which has to be now written
> as two separate calls, which is a slight a performance hit of
> having to read the 1.6MB cache twice. Maybe it does not matter,
> or the new usage's convenience outweighs it; I cannot tell
> offhand.
Sort all the forced removals to the end. I think it's still better than
git-update-cache --force-remove one --force-remove two \
--force-remove three ...
--
Petr "Pasky" Baudis
Stuff: http://pasky.or.cz/
C++: an octopus made by nailing extra legs onto a dog. -- Steve Taylor
^ permalink raw reply
* [PATCH] cg-init: remove .git/HEAD symlink creation
From: Jonas Fonseca @ 2005-05-31 20:47 UTC (permalink / raw)
To: Petr Baudis; +Cc: git
Remove symlink creation which git-init-db now creates.
Signed-off-by: Jonas Fonseca <fonseca@diku.dk>
---
cg-init | 1 -
1 files changed, 1 deletion(-)
diff --git a/cg-init b/cg-init
--- a/cg-init
+++ b/cg-init
@@ -24,7 +24,6 @@ trap "rm -rf $_git" SIGTERM EXIT
git-init-db
mkdir $_git/branches
touch $_git/refs/heads/master
-ln -s refs/heads/master $_git/HEAD
if [ "$uri" ]; then
echo "$uri" >$_git/branches/origin
--
Jonas Fonseca
^ permalink raw reply
* Mercurial 0.5b vs git
From: Matt Mackall @ 2005-05-31 21:31 UTC (permalink / raw)
To: Linus Torvalds; +Cc: mercurial, git, linux-kernel
The latest version of Mercurial is available at:
http://selenic.com/mercurial/
Utilities to convert git repos and interoperate with git are beginning
to appear on the mercurial mailing list, including a port of gitk.
As a practical demonstration, I've imported Ingo's BKCVS patchset into
Mercurial. The result is a 297M archive with 28237 changesets going back
to 2.4.0. Some history is lost because of the BK->CVS flattening. You
can browse it here:
http://userweb.kernel.org/~mpm/linux-hg/index.cgi
Be sure to check out the annotate feature. Unfortunately there are no
branches in this repo because of the BK->CVS flattening, but you can
look at the main Mercurial repo to see examples of pulls.
The full tarball of the Mercurial kernel repo (144MB) can be grabbed here:
http://www.kernel.org/pub/linux/kernel/people/mpm/linux-hg.tar.gz
If you want to browse this repo on your own machine (very fast and
convenient for laptops!), simply install Mercurial, download the
tarball, run 'hg serve' in the repo directory and point your web
browser at http://localhost:8000.
The web interface also serves as a highly efficient merge server:
$ time hg -v merge http://remotehost:8000/
searching for changes
adding changesets
adding manifests
adding files
118549846 bytes of data transfered
modified 23306 files, added 28238 changesets and 188476 new revisions
real 4m51.371s
user 1m25.852s
sys 0m8.303s
That's pulling the whole kernel history over fast DSL with only 113M
of traffic. Compare that to the 2.6.11 tar.bz2 at 35M. Smaller merges
are of course proportionally faster. (Pulls from userweb.kernel.org
are disabled because the machine has limited bandwidth.)
Verifying the archive:
$ time hg verify
checking changesets
checking manifests
crosschecking files in changesets and manifests
checking files
23305 files, 28238 changesets, 188464 total revisions
real 2m48.986s
user 1m30.055s
sys 0m7.158s
Checking the integrity of the equivalent git archive looks like it
will take an hour or more of seek intensive I/O (though the person
who was timing it for me gave up).
This highlights one of git's most serious problems: storing the
repository by hash. This tends to pessimize layout over time. Initial
check-ins will be nicely ordered by write order, but as changes are
made, the set of files in the tip will get spread further and further
apart on the disk and in more and more random order. Copying the
archive via rsync, cp -a, or the like will tend to exacerbate things
by reordering _everything_ in hash (aka worst possible) order. This is
pretty fundamental to the git design and will cause its scalability to
fall apart as the number of revisions mount.
Mercurial was originally using a similar scheme, and when I ran into
this problem, I spent a day playing with variations on sorting by
inode, prefetching, etc to get the performance back. None of it came
close to the performance of simply having everything layed out well on
disk in the first place.
My eventual solution was a simple 5-line change to switch back to a
tree-structured repo layout like CVS. This lets the filesystem block
allocator assist by putting files in the same directory near each
other on disk. Also, copying repos tends to optimize things rather
than making things worse. Mercurial also inherently stores all file
revisions together so operations like tree diffs or file annotate can
be done with a minimum of seeking.
Here's a quick comparison:
Mercurial git BK (*)
storage revlog delta compressed revisions SCCS weave
storage naming by filename by revision hash by filename
merge file DAGs changeset DAG file DAGs?
consistency SHA1 SHA1 CRC
signable? yes yes no
retrieve file tip O(1) O(1) O(revs)
add rev O(1) O(1) O(revs)
find prev file rev O(1) O(changesets) O(revs)
annotate file O(revs) O(changesets) O(revs)
find file changeset O(1) O(changesets) ?
file tracking stat-based stat-based bk edit
checkout O(files) O(files) O(revs)?
commit O(changes) O(changes) ?
6 patches/s 6 patches/s slow
diff working dir O(changes) O(changes) ?
< 1s < 1s ?
tree diff revs O(changes) O(changes) ?
< 1s < 1s ?
hardlink clone O(files) O(revisions) O(files)
find remote csets O(log new) rsync: O(revisions) ?
git-http: O(changesets)
pull remote csets O(patch) O(modified files) O(patch)
repo growth O(patch) O(revisions) O(patch)
kernel history 297M 3.5G? 250M?
lines of code 3700 6500+cogito+gitweb+.. ??
* I've never used BK so this is just guesses
--
Mathematics is the supreme nostalgia of our time.
^ permalink raw reply
* [PATCH 1/2] diff: consolidate test helper script pieces.
From: Junio C Hamano @ 2005-05-31 21:47 UTC (permalink / raw)
To: Linus Torvalds; +Cc: Git Mailing List
In-Reply-To: <Pine.LNX.4.58.0505310827330.1876@ppc970.osdl.org>
There were duplicate script pieces to help comparing diff
output, which this patch consolidates into the t/diff-lib.sh
library.
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
*** The next one is to fix the pathspec, whose test relies on
*** this cleanup.
t/diff-lib.sh | 35 +++++++++++++++++++++++++++++++++++
t/t4003-diff-rename-1.sh | 9 +--------
t/t4005-diff-rename-2.sh | 23 +----------------------
t/t4007-rename-3.sh | 15 +--------------
t/t4008-diff-break-rewrite.sh | 15 +--------------
t/t4009-diff-rename-4.sh | 29 ++++-------------------------
6 files changed, 43 insertions(+), 83 deletions(-)
diff --git a/t/diff-lib.sh b/t/diff-lib.sh
new file mode 100644
--- /dev/null
+++ b/t/diff-lib.sh
@@ -0,0 +1,35 @@
+:
+
+_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
+_x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
+sanitize_diff_raw='/^:/s/ '"$_x40"' '"$_x40"' \([A-Z]\)[0-9]* / X X \1# /'
+compare_diff_raw () {
+ # When heuristics are improved, the score numbers would change.
+ # Ignore them while comparing.
+ # Also we do not check SHA1 hash generation in this test, which
+ # is a job for t0000-basic.sh
+
+ sed -e "$sanitize_diff_raw" <"$1" >.tmp-1
+ sed -e "$sanitize_diff_raw" <"$2" >.tmp-2
+ diff -u .tmp-1 .tmp-2 && rm -f .tmp-1 .tmp-2
+}
+
+sanitize_diff_raw_z='/^:/s/ '"$_x40"' '"$_x40"' \([A-Z]\)[0-9]*$/ X X \1#/'
+compare_diff_raw_z () {
+ # When heuristics are improved, the score numbers would change.
+ # Ignore them while comparing.
+ # Also we do not check SHA1 hash generation in this test, which
+ # is a job for t0000-basic.sh
+
+ tr '\0' '\012' <"$1" | sed -e "$sanitize_diff_raw_z" >.tmp-1
+ tr '\0' '\012' <"$2" | sed -e "$sanitize_diff_raw_z" >.tmp-2
+ diff -u .tmp-1 .tmp-2 && rm -f .tmp-1 .tmp-2
+}
+
+compare_diff_patch () {
+ # When heuristics are improved, the score numbers would change.
+ # Ignore them while comparing.
+ sed -e '/^[dis]*imilarity index [0-9]*%$/d' <"$1" >.tmp-1
+ sed -e '/^[dis]*imilarity index [0-9]*%$/d' <"$2" >.tmp-2
+ diff -u .tmp-1 .tmp-2 && rm -f .tmp-1 .tmp-2
+}
diff --git a/t/t4003-diff-rename-1.sh b/t/t4003-diff-rename-1.sh
--- a/t/t4003-diff-rename-1.sh
+++ b/t/t4003-diff-rename-1.sh
@@ -7,14 +7,7 @@ test_description='More rename detection
'
. ./test-lib.sh
-
-compare_diff_patch () {
- # When heuristics are improved, the score numbers would change.
- # Ignore them while comparing.
- sed -e '/^similarity index [0-9]*%$/d' <"$1" >.tmp-1
- sed -e '/^similarity index [0-9]*%$/d' <"$2" >.tmp-2
- diff -u .tmp-1 .tmp-2 && rm -f .tmp-1 .tmp-2
-}
+. ../diff-lib.sh ;# test-lib chdir's into trash
test_expect_success \
'prepare reference tree' \
diff --git a/t/t4005-diff-rename-2.sh b/t/t4005-diff-rename-2.sh
--- a/t/t4005-diff-rename-2.sh
+++ b/t/t4005-diff-rename-2.sh
@@ -7,28 +7,7 @@ test_description='Same rename detection
'
. ./test-lib.sh
-
-_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
-_x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
-sanitize_diff_raw='s/ '"$_x40"' '"$_x40"' \([A-Z]\)[0-9]* / X X \1# /'
-compare_diff_raw () {
- # When heuristics are improved, the score numbers would change.
- # Ignore them while comparing.
- # Also we do not check SHA1 hash generation in this test, which
- # is a job for t0000-basic.sh
-
- sed -e "$sanitize_diff_raw" <"$1" >.tmp-1
- sed -e "$sanitize_diff_raw" <"$2" >.tmp-2
- diff -u .tmp-1 .tmp-2 && rm -f .tmp-1 .tmp-2
-}
-
-compare_diff_patch () {
- # When heuristics are improved, the score numbers would change.
- # Ignore them while comparing.
- sed -e '/^similarity index [0-9]*%$/d' <"$1" >.tmp-1
- sed -e '/^similarity index [0-9]*%$/d' <"$2" >.tmp-2
- diff -u .tmp-1 .tmp-2 && rm -f .tmp-1 .tmp-2
-}
+. ../diff-lib.sh ;# test-lib chdir's into trash
test_expect_success \
'prepare reference tree' \
diff --git a/t/t4007-rename-3.sh b/t/t4007-rename-3.sh
--- a/t/t4007-rename-3.sh
+++ b/t/t4007-rename-3.sh
@@ -7,20 +7,7 @@ test_description='Rename interaction wit
'
. ./test-lib.sh
-
-_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
-_x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
-sanitize_diff_raw='s/ '"$_x40"' '"$_x40"' \([A-Z]\)[0-9]* / X X \1# /'
-compare_diff_raw () {
- # When heuristics are improved, the score numbers would change.
- # Ignore them while comparing.
- # Also we do not check SHA1 hash generation in this test, which
- # is a job for t0000-basic.sh
-
- sed -e "$sanitize_diff_raw" <"$1" >.tmp-1
- sed -e "$sanitize_diff_raw" <"$2" >.tmp-2
- diff -u .tmp-1 .tmp-2 && rm -f .tmp-1 .tmp-2
-}
+. ../diff-lib.sh ;# test-lib chdir's into trash
test_expect_success \
'prepare reference tree' \
diff --git a/t/t4008-diff-break-rewrite.sh b/t/t4008-diff-break-rewrite.sh
--- a/t/t4008-diff-break-rewrite.sh
+++ b/t/t4008-diff-break-rewrite.sh
@@ -22,20 +22,7 @@ four changes in total.
Further, with -B and -M together, these should turn into two renames.
'
. ./test-lib.sh
-
-_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
-_x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
-sanitize_diff_raw='s/ '"$_x40"' '"$_x40"' \([CDNR]\)[0-9]* / X X \1# /'
-compare_diff_raw () {
- # When heuristics are improved, the score numbers would change.
- # Ignore them while comparing.
- # Also we do not check SHA1 hash generation in this test, which
- # is a job for t0000-basic.sh
-
- sed -e "$sanitize_diff_raw" <"$1" >.tmp-1
- sed -e "$sanitize_diff_raw" <"$2" >.tmp-2
- diff -u .tmp-1 .tmp-2 && rm -f .tmp-1 .tmp-2
-}
+. ../diff-lib.sh ;# test-lib chdir's into trash
test_expect_success \
setup \
diff --git a/t/t4009-diff-rename-4.sh b/t/t4009-diff-rename-4.sh
--- a/t/t4009-diff-rename-4.sh
+++ b/t/t4009-diff-rename-4.sh
@@ -7,28 +7,7 @@ test_description='Same rename detection
'
. ./test-lib.sh
-
-_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
-_x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
-sanitize_diff_raw='/^:/s/ '"$_x40"' '"$_x40"' \([A-Z]\)[0-9]*$/ X X \1#/'
-compare_diff_raw () {
- # When heuristics are improved, the score numbers would change.
- # Ignore them while comparing.
- # Also we do not check SHA1 hash generation in this test, which
- # is a job for t0000-basic.sh
-
- tr '\0' '\012' <"$1" | sed -e "$sanitize_diff_raw" >.tmp-1
- tr '\0' '\012' <"$2" | sed -e "$sanitize_diff_raw" >.tmp-2
- diff -u .tmp-1 .tmp-2 && rm -f .tmp-1 .tmp-2
-}
-
-compare_diff_patch () {
- # When heuristics are improved, the score numbers would change.
- # Ignore them while comparing.
- sed -e '/^similarity index [0-9]*%$/d' <"$1" >.tmp-1
- sed -e '/^similarity index [0-9]*%$/d' <"$2" >.tmp-2
- diff -u .tmp-1 .tmp-2 && rm -f .tmp-1 .tmp-2
-}
+. ../diff-lib.sh ;# test-lib chdir's into trash
test_expect_success \
'prepare reference tree' \
@@ -63,7 +42,7 @@ EOF
test_expect_success \
'validate output from rename/copy detection (#1)' \
- 'compare_diff_raw current expected'
+ 'compare_diff_raw_z current expected'
# make sure diff-helper can grok it.
mv current diff-raw
@@ -120,7 +99,7 @@ EOF
test_expect_success \
'validate output from rename/copy detection (#2)' \
- 'compare_diff_raw current expected'
+ 'compare_diff_raw_z current expected'
# make sure diff-helper can grok it.
mv current diff-raw
@@ -173,7 +152,7 @@ EOF
test_expect_success \
'validate output from rename/copy detection (#3)' \
- 'compare_diff_raw current expected'
+ 'compare_diff_raw_z current expected'
# make sure diff-helper can grok it.
mv current diff-raw
------------
^ permalink raw reply
* [PATCH] ls-tree: remove trailing slashes properly.
From: Junio C Hamano @ 2005-05-31 21:49 UTC (permalink / raw)
To: Linus Torvalds; +Cc: Git Mailing List
In-Reply-To: <Pine.LNX.4.58.0505310827330.1876@ppc970.osdl.org>
A typo prevented trailing slashes from being removed properly.
This fixes the problem that "drivers/char" which is a tree was
not shown when "drivers/char/" is given.
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
t/t3100-ls-tree-restrict.sh | 32 +++++++++++++++++++++++++++-----
ls-tree.c | 2 +-
2 files changed, 28 insertions(+), 6 deletions(-)
diff --git a/t/t3100-ls-tree-restrict.sh b/t/t3100-ls-tree-restrict.sh
--- a/t/t3100-ls-tree-restrict.sh
+++ b/t/t3100-ls-tree-restrict.sh
@@ -14,7 +14,7 @@ This test runs git-ls-tree with the foll
path2/baz/b - a file in a directory in a directory
The new path restriction code should do the right thing for path2 and
-path2/baz
+path2/baz. Also path0/ should snow nothing.
'
. ./test-lib.sh
@@ -63,7 +63,7 @@ EOF
test_output'
test_expect_success \
- 'ls-tree filtered' \
+ 'ls-tree filtered with path' \
'git-ls-tree $tree path >current &&
cat >expected <<\EOF &&
EOF
@@ -71,7 +71,7 @@ EOF
test_expect_success \
- 'ls-tree filtered' \
+ 'ls-tree filtered with path1 path0' \
'git-ls-tree $tree path1 path0 >current &&
cat >expected <<\EOF &&
120000 blob X path1
@@ -80,7 +80,7 @@ EOF
test_output'
test_expect_success \
- 'ls-tree filtered' \
+ 'ls-tree filtered with path2' \
'git-ls-tree $tree path2 >current &&
cat >expected <<\EOF &&
040000 tree X path2
@@ -91,7 +91,7 @@ EOF
test_output'
test_expect_success \
- 'ls-tree filtered' \
+ 'ls-tree filtered with path2/baz' \
'git-ls-tree $tree path2/baz >current &&
cat >expected <<\EOF &&
040000 tree X path2/baz
@@ -99,4 +99,26 @@ test_expect_success \
EOF
test_output'
+test_expect_success \
+ 'ls-tree filtered with path2' \
+ 'git-ls-tree $tree path2 >current &&
+ cat >expected <<\EOF &&
+040000 tree X path2
+040000 tree X path2/baz
+120000 blob X path2/bazbo
+100644 blob X path2/foo
+EOF
+ test_output'
+
+test_expect_success \
+ 'ls-tree filtered with path2/' \
+ 'git-ls-tree $tree path2/ >current &&
+ cat >expected <<\EOF &&
+040000 tree X path2
+040000 tree X path2/baz
+120000 blob X path2/bazbo
+100644 blob X path2/foo
+EOF
+ test_output'
+
test_done
diff --git a/ls-tree.c b/ls-tree.c
--- a/ls-tree.c
+++ b/ls-tree.c
@@ -201,7 +201,7 @@ static int list(char **path)
int err = 0;
for (i = 0; path[i]; i++) {
int len = strlen(path[i]);
- while (0 <= len && path[i][len] == '/')
+ while (0 < len && path[i][len-1] == '/')
len--;
err = err | list_one(path[i], path[i] + len);
}
------------
^ permalink raw reply
* [PATCH 2/2] diff: Fix trailing slash handling.
From: Junio C Hamano @ 2005-05-31 21:48 UTC (permalink / raw)
To: Linus Torvalds; +Cc: Git Mailing List
In-Reply-To: <Pine.LNX.4.58.0505310827330.1876@ppc970.osdl.org>
When limiting the world to "drivers/char/", we should not
consider "drivers/char" which is not a directory.
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
t/t4010-diff-pathspec.sh | 65 ++++++++++++++++++++++++++++++++++++++++++++++
diffcore-pathspec.c | 60 +++++++++++++++++++++++++++++++++++++------
2 files changed, 117 insertions(+), 8 deletions(-)
diff --git a/t/t4010-diff-pathspec.sh b/t/t4010-diff-pathspec.sh
new file mode 100644
--- /dev/null
+++ b/t/t4010-diff-pathspec.sh
@@ -0,0 +1,65 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='Pathspec restrictions
+
+Prepare:
+ file0
+ path1/file1
+'
+. ./test-lib.sh
+. ../diff-lib.sh ;# test-lib chdir's into trash
+
+test_expect_success \
+ setup \
+ 'echo frotz >file0 &&
+ mkdir path1 &&
+ echo rezrov >path1/file1 &&
+ git-update-cache --add file0 path1/file1 &&
+ tree=`git-write-tree` &&
+ echo "$tree" &&
+ echo nitfol >file0 &&
+ echo yomin >path1/file1 &&
+ git-update-cache file0 path1/file1'
+
+cat >expected <<\EOF
+EOF
+test_expect_success \
+ 'limit to path should show nothing' \
+ 'git-diff-cache --cached $tree path >current &&
+ compare_diff_raw current expected'
+
+cat >expected <<\EOF
+:100644 100644 766498d93a4b06057a8e49d23f4068f1170ff38f 0a41e115ab61be0328a19b29f18cdcb49338d516 M path1/file1
+EOF
+test_expect_success \
+ 'limit to path1 should show path1/file1' \
+ 'git-diff-cache --cached $tree path1 >current &&
+ compare_diff_raw current expected'
+
+cat >expected <<\EOF
+:100644 100644 766498d93a4b06057a8e49d23f4068f1170ff38f 0a41e115ab61be0328a19b29f18cdcb49338d516 M path1/file1
+EOF
+test_expect_success \
+ 'limit to path1/ should show path1/file1' \
+ 'git-diff-cache --cached $tree path1/ >current &&
+ compare_diff_raw current expected'
+
+cat >expected <<\EOF
+:100644 100644 766498d93a4b06057a8e49d23f4068f1170ff38f 0a41e115ab61be0328a19b29f18cdcb49338d516 M file0
+EOF
+test_expect_success \
+ 'limit to file0 should show file0' \
+ 'git-diff-cache --cached $tree file0 >current &&
+ compare_diff_raw current expected'
+
+cat >expected <<\EOF
+EOF
+test_expect_success \
+ 'limit to file0/ should emit nothing.' \
+ 'git-diff-cache --cached $tree file0/ >current &&
+ compare_diff_raw current expected'
+
+test_done
diff --git a/diffcore-pathspec.c b/diffcore-pathspec.c
--- a/diffcore-pathspec.c
+++ b/diffcore-pathspec.c
@@ -8,9 +8,13 @@
struct path_spec {
const char *spec;
int len;
+ int reject_non_tree;
};
-static int matches_pathspec(const char *name, struct path_spec *s, int cnt)
+static int matches_pathspec(const char *name,
+ int obviously_non_tree,
+ struct path_spec *s,
+ int cnt)
{
int i;
int namelen;
@@ -21,10 +25,25 @@ static int matches_pathspec(const char *
namelen = strlen(name);
for (i = 0; i < cnt; i++) {
int len = s[i].len;
- if (! strncmp(s[i].spec, name, len) &&
- len <= namelen &&
- (name[len] == 0 || name[len] == '/'))
- return 1;
+ if (!strncmp(s[i].spec, name, len)) {
+ /* Leading part matches. */
+ if (len == namelen) {
+ /* Exact match: spec "drivers/char/"
+ * should not match against path
+ * "drivers/char".
+ */
+ if (s[i].reject_non_tree && obviously_non_tree)
+ continue;
+ return 1;
+ }
+ else if ((len < namelen) && name[len] == '/') {
+ /* Spec is leading path */
+ return 1;
+ }
+ /* otherwise, it is a false match of spec "abc"
+ * against path "abcdefg", so we continue.
+ */
+ }
}
return 0;
}
@@ -47,14 +66,39 @@ void diffcore_pathspec(const char **path
int l;
spec[i].spec = pathspec[i];
l = strlen(pathspec[i]);
- while (l > 0 && pathspec[i][l-1] == '/')
- l--;
+ if (l > 0 && pathspec[i][l-1] == '/') {
+ spec[i].reject_non_tree = 1;
+ while (l > 0 && pathspec[i][l-1] == '/')
+ l--;
+ }
spec[i].len = l;
}
for (i = 0; i < q->nr; i++) {
struct diff_filepair *p = q->queue[i];
- if (matches_pathspec(p->two->path, spec, speccnt))
+ int obviously_non_tree;
+#ifndef DIFF_TREE_CALLS_PATHSPEC
+ /*
+ * NOTE: This relies on the current behaviour of
+ * diff-tree which does not use diffcore-pathspec
+ * and we would not ever see "tree" objects
+ * in our input.
+ */
+ obviously_non_tree = 1;
+#else
+ if (DIFF_FILE_VALID(p->two))
+ obviously_non_tree = !S_ISDIR(p->two->mode);
+ else if (DIFF_FILE_VALID(p->one))
+ /* path is being deleted. is it a tree? */
+ obviously_non_tree = !S_ISDIR(p->one->mode);
+ else
+ /* path is unmerged, which comes from cache
+ * so it cannot be a tree
+ */
+ obviously_non_tree = 1;
+#endif
+ if (matches_pathspec(p->two->path, obviously_non_tree,
+ spec, speccnt))
diff_q(&outq, p);
else
diff_free_filepair(p);
------------
^ permalink raw reply
* [PATCH] Add git-format-patch-script.
From: Junio C Hamano @ 2005-05-31 21:50 UTC (permalink / raw)
To: Linus Torvalds; +Cc: git
The script takes two HEADs and optionally one directory name,
and prepares a patch file for each commit between the named
HEADS in a separate file in the named directory. The directory
defaults to the $cwd.
This is in the same spirit as the recent additions of helper
scripts to make core GIT plumbing more comfortable to use.
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
Makefile | 3 +-
git-format-patch-script | 66 +++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 68 insertions(+), 1 deletions(-)
diff --git a/Makefile b/Makefile
--- a/Makefile
+++ b/Makefile
@@ -22,7 +22,8 @@ INSTALL=install
SCRIPTS=git-apply-patch-script git-merge-one-file-script git-prune-script \
git-pull-script git-tag-script git-resolve-script git-whatchanged \
- git-deltafy-script git-fetch-script git-status-script git-commit-script
+ git-deltafy-script git-fetch-script git-status-script \
+ git-commit-script git-format-patch-script
PROG= git-update-cache git-diff-files git-init-db git-write-tree \
git-read-tree git-commit-tree git-cat-file git-fsck-cache \
diff --git a/git-format-patch-script b/git-format-patch-script
new file mode 100755
--- /dev/null
+++ b/git-format-patch-script
@@ -0,0 +1,66 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+junio="$1"
+linus="$2"
+outdir="${3:-./}"
+
+tmp=.tmp-series$$
+trap 'rm -f $tmp-*' 0 1 2 3 15
+
+series=$tmp-series
+
+titleScript='
+ 1,/^$/d
+ : loop
+ /^$/b loop
+ s/[^-a-z.A-Z_0-9]/-/g
+ s/^--*//g
+ s/--*$//g
+ s/---*/-/g
+ s/$/.txt/
+ s/\.\.\.*/\./g
+ q
+'
+
+_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
+_x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
+stripCommitHead='/^'"$_x40"' (from '"$_x40"')$/d'
+
+O=
+if test -f .git/patch-order
+then
+ O=-O.git/patch-order
+fi
+git-rev-list "$junio" "$linus" >$series
+total=`wc -l <$series`
+i=$total
+while read commit
+do
+ title=`git-cat-file commit "$commit" | sed -e "$titleScript"`
+ num=`printf "%d/%d" $i $total`
+ file=`printf '%04d-%s' $i "$title"`
+ i=`expr "$i" - 1`
+ echo "$file"
+ {
+ mailScript='
+ 1,/^$/d
+ : loop
+ /^$/b loop
+ s|^|[PATCH '"$num"'] |
+ : body
+ p
+ n
+ b body'
+
+ git-cat-file commit "$commit" | sed -ne "$mailScript"
+ echo '---'
+ echo
+ git-diff-tree -p $O "$commit" | git-apply --stat
+ echo
+ git-diff-tree -p $O "$commit" | sed -e "$stripCommitHead"
+ echo '------------'
+ } >"$outdir$file"
+done <$series
+
------------
^ permalink raw reply
* [COGITO PATCH] Heads and tags in subdirectories
From: Santi Béjar @ 2005-05-31 22:00 UTC (permalink / raw)
To: Git Mailing List
Keep heads and tags in their respective subtirectoris named as
the branch. This fixes the case where two repositoris have tags
with the same name.
Add a "-a" flag to cg-pull to download all the repositories heads,
so you can now do a "cg-log -r repo#branch" (cg-Xnormid repo#branch
job).
The transition is automatic when you do the first "cg-pull repo".
Signed-off-by: "Santi Béjar" <sbejar@gmmail.es>
cg-Xnormid | 14 ++++++++++-
cg-commit | 7 +++++
cg-pull | 72 +++++++++++++++++++++++++++++++++++++------------------------
3 files changed, 62 insertions(+), 31 deletions(-)
diff --git a/cg-Xnormid b/cg-Xnormid
--- a/cg-Xnormid
+++ b/cg-Xnormid
@@ -16,15 +16,25 @@
id="$1"
+repo=$(echo $id | cut -d '#' -f 1)
+(echo $repo | egrep -qv '[^a-zA-Z0-9_.@!:-]') || \
+ die "name contains invalid characters"
+id=$(echo $id | sed 's@#@/@')
+
if [ ! "$id" ] || [ "$id" = "this" ] || [ "$id" = "HEAD" ]; then
read id < "$_git/HEAD"
-elif [ -r "$_git/refs/tags/$id" ]; then
+elif [ -r "$_git/refs/tags/$id" ] && [ ! -d "$_git/refs/tags/$id" ]; then
read id < "$_git/refs/tags/$id"
-elif [ -r "$_git/refs/heads/$id" ]; then
+elif [ -r "$_git/refs/heads/$id" ] && [ ! -d "$_git/refs/tags/$id" ]; then
read id < "$_git/refs/heads/$id"
+elif [ -r "$_git/branches/$id" ]; then
+ repobranch=$(cat "$_git/branches/$id" | cut -d '#' -f 2 -s)
+ repobranch=${repobranch:-master}
+ read id < "$_git/refs/heads/$id/$repobranch"
+
# Short id's must be lower case and at least 4 digits.
elif [[ "$id" == [0-9a-z][0-9a-z][0-9a-z][0-9a-z]* ]]; then
idpref=${id:0:2}
diff --git a/cg-commit b/cg-commit
--- a/cg-commit
+++ b/cg-commit
@@ -141,7 +141,12 @@ if [ "$merging" ]; then
[ "$msgs" ] && echo -n 'Merge with '
[ -s $_git/merging-sym ] || cp $_git/merging $_git/merging-sym
for sym in $(cat $_git/merging-sym); do
- uri=$(cat $_git/branches/$sym)
+ repo=$(echo $sym | cut -d '#' -f 1)
+ branch=$(echo $sym | cut -d '#' -f 2 -s)
+ uri=$(cat $_git/branches/$repo)
+ uribranch=$(echo $uri | cut -d '#' -f 2 -s)
+ [ -z "$uribranch" ] && [ -n "$branch" ] &&
+ [ "$branch" != master ] && uri=${uri}#$branch
[ "$uri" ] || uri="$sym"
echo "$uri" >>$LOGMSG
[ "$msgs" ] && echo "$uri"
diff --git a/cg-pull b/cg-pull
--- a/cg-pull
+++ b/cg-pull
@@ -6,23 +6,41 @@
# Takes the branch name as an argument, defaulting to "origin".
#
# See `cg-branch-add` for some description.
+#
+# OPTIONS
+# -------
+# -a::
+# Pull all the heads from repositori.
-USAGE="cg-pull [BRANCH_NAME]"
+USAGE="cg-pull [-a] [BRANCH_NAME]"
. ${COGITO_LIB}cg-Xlib
-name=$1
-
+[ "$1" == "-a" ] && all=yes && shift
+name=$1 && shift
[ "$name" ] || { [ -s $_git/refs/heads/origin ] && name=origin; }
[ "$name" ] || die "where to pull from?"
-uri=$(cat "$_git/branches/$name" 2>/dev/null) || die "unknown branch: $name"
-rembranch=master
+repo=$(echo $name | cut -d '#' -f 1)
+repobranch=$(echo $name | cut -s -d '#' -f 2)
+
+uri=$(cat "$_git/branches/$name" 2>/dev/null) || die "unknown branch: $name"
if echo "$uri" | grep -q '#'; then
+ [ -z "$repobranch" ] && repobranch=$(echo $uri | cut -d '#' -f 2)
rembranch=$(echo $uri | cut -d '#' -f 2)
uri=$(echo $uri | cut -d '#' -f 1)
fi
+repobranch=${repobranch:-master}
+branch=$repo/$repobranch
+[ "$all" ] && repobranch=
+
+# So long we have:
+# $repo = name of the repositori
+# $uri = uri of the repositori
+# $repobranch = name of the branch in the repositori
+# empty if we want all the branches
+# $branch = name of the local branch in refs/heads/
pull_progress() {
percentage=""
@@ -232,39 +250,37 @@ fi
orig_head=
-[ -s "$_git/refs/heads/$name" ] && orig_head=$(cat "$_git/refs/heads/$name")
-
+[ -s "$_git/refs/heads/$branch" ] && orig_head=$(cat "$_git/refs/heads/$branch")
-mkdir -p $_git/refs/heads
-rsyncerr=
-$fetch -i "$uri/refs/heads/$rembranch" "$_git/refs/heads/$name" || rsyncerr=1
-if [ "$rsyncerr" ]; then
- rsyncerr=
- $fetch -s "$uri/heads/$rembranch" "$_git/refs/heads/$name" || rsyncerr=1
-fi
-if [ "$rsyncerr" ] && [ "$rembranch" = "master" ]; then
- rsyncerr=
- $fetch -s "$uri/HEAD" "$_git/refs/heads/$name" || rsyncerr=1
+# 2005/05 Convert old layout
+[ -f $_git/refs/heads/$repo ] && orig_head=$(cat $_git/refs/heads/$repo) &&
+rm -f $_git/refs/heads/$repo
+
+mkdir -p $_git/refs/heads/$repo
+if [ "$repobranch" ] ; then
+ $fetch -i "$uri/refs/heads/$repobranch" "$_git/refs/heads/$branch" ||
+ $fetch -s "$uri/heads/$repobranch" "$_git/refs/heads/$branch" ||
+ { [ "$repobranch" = "master" ] && $fetch -s "$uri/HEAD" "$_git/refs/heads/$branch"; } ||
+ rsyncerr=1
+else
+ $fetch -i -d "$uri/refs/heads" "$_git/refs/heads/$repo" ||
+ $fetch -s -d "$uri/heads" "$_git/refs/heads/$repo" ||
+ rsyncerr=1
fi
-[ "$rsyncerr" ] && die "unable to get the head pointer of branch $rembranch"
+[ "$rsyncerr" ] && die "unable to get the head pointer of branch $repobranch"
[ -d $_git_objects ] || mkdir -p $_git_objects
-$pull "$name" "$uri" || die "objects pull failed"
+$pull "$branch" "$uri" || die "objects pull failed"
-# FIXME: Warn about conflicting tag names?
# XXX: We now throw stderr to /dev/null since not all repositories
# may have tags/ and users were confused by the harmless errors.
-[ -d $_git/refs/tags ] || mkdir -p $_git/refs/tags
+[ -d $_git/refs/tags/$repo ] || mkdir -p $_git/refs/tags/$repo
rsyncerr=
-$fetch -i -s -u -d "$uri/refs/tags" "$_git/refs/tags" || rsyncerr=1
-if [ "$rsyncerr" ]; then
- rsyncerr=
- $fetch -i -s -u -d "$uri/tags" "$_git/refs/tags" || rsyncerr=1
-fi
+$fetch -i -s -u -d "$uri/refs/tags" "$_git/refs/tags/$repo" ||
+$fetch -i -s -u -d "$uri/tags" "$_git/refs/tags/$repo" || rsyncerr=1
[ "$rsyncerr" ] && echo "unable to get tags list (non-fatal)" >&2
-
-new_head=$(cat "$_git/refs/heads/$name")
+new_head=$(cat "$_git/refs/heads/$branch")
if [ ! "$orig_head" ]; then
echo "New branch: $new_head"
^ permalink raw reply
* Re: [PATCH] ls-tree: remove trailing slashes properly.
From: Linus Torvalds @ 2005-05-31 22:19 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Git Mailing List
In-Reply-To: <7vvf4zvzfw.fsf@assigned-by-dhcp.cox.net>
On Tue, 31 May 2005, Junio C Hamano wrote:
>
> A typo prevented trailing slashes from being removed properly.
This is still wrong.
> This fixes the problem that "drivers/char" which is a tree was
> not shown when "drivers/char/" is given.
I sent an earlier email pointing out that removing trailing slashes is
_incorrect_, because it means that "drivers/char/" will match the _file_
"drivers/char", which is wrong.
IOW, the trailing slash should not be removed, it's the _test_ that is
wrong.
I just checked in a fix for this in diffcore-patchspec.c, I'd hope that
ls-tree could get it right too. Removing trailing slashes is a bandaid
that hides one bug by making it appear as a different bug.
Linus
^ permalink raw reply
* Re: [PATCH] ls-tree: remove trailing slashes properly.
From: Junio C Hamano @ 2005-05-31 22:35 UTC (permalink / raw)
To: Linus Torvalds; +Cc: Git Mailing List
In-Reply-To: <Pine.LNX.4.58.0505311507010.1876@ppc970.osdl.org>
>>>>> "LT" == Linus Torvalds <torvalds@osdl.org> writes:
LT> I just checked in a fix for this in diffcore-patchspec.c,
LT> I'd hope that ls-tree could get it right too. Removing
LT> trailing slashes is a bandaid that hides one bug by making
LT> it appear as a different bug.
Yes, I am aware of that problem but have not touched it; I sent
an earlier email about this to you. While I was waiting for
your response, I found that drivers/char/ does not even match
drivers/char tree, which is what the patch you are responding to
is about. Will look into it later.
^ permalink raw reply
* Re: [SCRIPT] cg-rpush & locking
From: Nicolas Pitre @ 2005-05-31 23:16 UTC (permalink / raw)
To: Tony Lindgren; +Cc: git, Matthias Urlichs
In-Reply-To: <20050531190005.GE18723@atomide.com>
On Tue, 31 May 2005, Tony Lindgren wrote:
> Anybody have any better ideas for locking that also works with
> rsync?
Why do you need a lock at all?
Just update your HEAD reference last when you push and get it first when
you pull.
Nicolas
^ permalink raw reply
* [PATCH] ls-tree: handle trailing slashes in the pathspec properly.
From: Junio C Hamano @ 2005-05-31 23:18 UTC (permalink / raw)
To: Linus Torvalds; +Cc: Git Mailing List
In-Reply-To: <Pine.LNX.4.58.0505311507010.1876@ppc970.osdl.org>
>>>>> "LT" == Linus Torvalds <torvalds@osdl.org> writes:
LT> I just checked in a fix for this in diffcore-patchspec.c, I'd hope that
LT> ls-tree could get it right too. Removing trailing slashes is a bandaid
LT> that hides one bug by making it appear as a different bug.
I take it to mean that you took my other patch for diffcore-pathspec.
Here is a fixed ls-tree, with a couple of new tests in an
existing test script, to catch this bug.
------------
This fixes one problem while avoiding the same mistake earlier
diffcore-pathspec had:
- "drivers/char" which is a tree was not shown given
"drivers/char/".
- "drivers/char" which is not a tree is not shown given
"drivers/char/".
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
ls-tree.c | 57 ++++++++++++++++++++++++++-----------------
t/t3100-ls-tree-restrict.sh | 39 ++++++++++++++++++++++++++----
2 files changed, 68 insertions(+), 28 deletions(-)
diff --git a/ls-tree.c b/ls-tree.c
--- a/ls-tree.c
+++ b/ls-tree.c
@@ -54,13 +54,22 @@ static int prepare_children(struct tree_
return 0;
}
-static struct tree_entry_list *find_entry_0(struct tree_entry_list *elem,
- const char *path,
- const char *path_end)
+static struct tree_entry_list *find_entry(const char *path,
+ const char *path_end,
+ int require_tree)
{
- const char *ep;
int len;
+ struct tree_entry_list *elem = &root_entry;
+ const char *ep;
+ if (path == path_end)
+ /* Special. This is the root level */
+ return elem;
+
+ /* Find tree element, descending from root, that
+ * corresponds to the named path, lazily expanding
+ * the tree if possible.
+ */
while (path < path_end) {
if (prepare_children(elem))
return NULL;
@@ -81,27 +90,25 @@ static struct tree_entry_list *find_entr
break;
elem = elem->next;
}
- if (path_end <= ep || !elem)
+ if (!elem)
+ return NULL;
+ if (path_end <= ep) {
+ /* elem matches the specified path. However,
+ * if the user said "drivers/char/" and
+ * elem is "drivers/char", _and_ it is not
+ * a tree, then we should reject, just like
+ * "/bin/ls -a ls-tree.c/" says "Not a directory".
+ */
+ if (require_tree && !elem->directory)
+ return NULL;
return elem;
+ }
while (*ep == '/' && ep < path_end)
ep++;
path = ep;
}
return NULL;
-}
-static struct tree_entry_list *find_entry(const char *path,
- const char *path_end)
-{
- /* Find tree element, descending from root, that
- * corresponds to the named path, lazily expanding
- * the tree if possible.
- */
- if (path == path_end) {
- /* Special. This is the root level */
- return &root_entry;
- }
- return find_entry_0(&root_entry, path, path_end);
}
static void show_entry_name(struct tree_entry_list *e)
@@ -180,10 +187,10 @@ static int show_entry(struct tree_entry_
return err;
}
-static int list_one(const char *path, const char *path_end)
+static int list_one(const char *path, const char *path_end, int require_tree)
{
int err = 0;
- struct tree_entry_list *e = find_entry(path, path_end);
+ struct tree_entry_list *e = find_entry(path, path_end, require_tree);
if (!e) {
/* traditionally ls-tree does not complain about
* missing path. We may change this later to match
@@ -201,9 +208,13 @@ static int list(char **path)
int err = 0;
for (i = 0; path[i]; i++) {
int len = strlen(path[i]);
- while (0 <= len && path[i][len] == '/')
- len--;
- err = err | list_one(path[i], path[i] + len);
+ int require_tree = 0;
+ if (0 < len && path[i][len-1] == '/') {
+ require_tree = 1;
+ while (0 < len && path[i][len-1] == '/')
+ len--;
+ }
+ err = err | list_one(path[i], path[i] + len, require_tree);
}
return err;
}
diff --git a/t/t3100-ls-tree-restrict.sh b/t/t3100-ls-tree-restrict.sh
--- a/t/t3100-ls-tree-restrict.sh
+++ b/t/t3100-ls-tree-restrict.sh
@@ -14,7 +14,7 @@ This test runs git-ls-tree with the foll
path2/baz/b - a file in a directory in a directory
The new path restriction code should do the right thing for path2 and
-path2/baz
+path2/baz. Also path0/ should snow nothing.
'
. ./test-lib.sh
@@ -63,7 +63,7 @@ EOF
test_output'
test_expect_success \
- 'ls-tree filtered' \
+ 'ls-tree filtered with path' \
'git-ls-tree $tree path >current &&
cat >expected <<\EOF &&
EOF
@@ -71,7 +71,7 @@ EOF
test_expect_success \
- 'ls-tree filtered' \
+ 'ls-tree filtered with path1 path0' \
'git-ls-tree $tree path1 path0 >current &&
cat >expected <<\EOF &&
120000 blob X path1
@@ -80,7 +80,14 @@ EOF
test_output'
test_expect_success \
- 'ls-tree filtered' \
+ 'ls-tree filtered with path0/' \
+ 'git-ls-tree $tree path0/ >current &&
+ cat >expected <<\EOF &&
+EOF
+ test_output'
+
+test_expect_success \
+ 'ls-tree filtered with path2' \
'git-ls-tree $tree path2 >current &&
cat >expected <<\EOF &&
040000 tree X path2
@@ -91,7 +98,7 @@ EOF
test_output'
test_expect_success \
- 'ls-tree filtered' \
+ 'ls-tree filtered with path2/baz' \
'git-ls-tree $tree path2/baz >current &&
cat >expected <<\EOF &&
040000 tree X path2/baz
@@ -99,4 +106,26 @@ test_expect_success \
EOF
test_output'
+test_expect_success \
+ 'ls-tree filtered with path2' \
+ 'git-ls-tree $tree path2 >current &&
+ cat >expected <<\EOF &&
+040000 tree X path2
+040000 tree X path2/baz
+120000 blob X path2/bazbo
+100644 blob X path2/foo
+EOF
+ test_output'
+
+test_expect_success \
+ 'ls-tree filtered with path2/' \
+ 'git-ls-tree $tree path2/ >current &&
+ cat >expected <<\EOF &&
+040000 tree X path2
+040000 tree X path2/baz
+120000 blob X path2/bazbo
+100644 blob X path2/foo
+EOF
+ test_output'
+
test_done
------------
^ permalink raw reply
* Re: [PATCH] ls-tree: remove trailing slashes properly.
From: Junio C Hamano @ 2005-05-31 23:22 UTC (permalink / raw)
To: Linus Torvalds; +Cc: Git Mailing List
In-Reply-To: <Pine.LNX.4.58.0505311507010.1876@ppc970.osdl.org>
Should we do the same for git-ls-files?
^ permalink raw reply
* Re: [PATCH] ls-tree: handle trailing slashes in the pathspec properly.
From: Linus Torvalds @ 2005-05-31 23:48 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Git Mailing List
In-Reply-To: <7v8y1vvvaa.fsf_-_@assigned-by-dhcp.cox.net>
On Tue, 31 May 2005, Junio C Hamano wrote:
>
> I take it to mean that you took my other patch for diffcore-pathspec.
No, I did my own. Rather than add more code to handle '/' as a special
case, I just removed it all, and fixed the compare logic.
> Here is a fixed ls-tree, with a couple of new tests in an
> existing test script, to catch this bug.
You seem to think that '/' at the end is a special case, and it really
shouldn't be. It should just fall out as a natural special case of a
zero-sized name (which is, btw, the same natural special case that the
path of "" should have in &root_dir).
For some reason your ls-tree.c logic seems to think that zero-sized names
means "root entry", when the _natural_ thing to do is to pass in the "base
directory", and then a zero-sized name is that base.
IOW, it would make much more sense to have
list_one(struct tree_entry_list *tree, const char *name)
{
const char *slash = strchr(name, '/');
const char *next;
int len;
for (;;) {
if (!slash) {
len = strlen(name);
next = NULL;
} else {
len = slash - name;
next = slash+1;
}
newtree = tree;
if (len)
newtree = lookup(tree, name, len);
if (!next)
break;
tree = newtree;
name = next;
}
/* Ok, "newtree" is the last component */
show_entry(newtree);
}
and then call it with
list_one(&root_entry, full_path)
and notice how the cases of an empty path "" and "xxx//yy" and "xx/"
just fall out from the exact same logic - a zero-sized name is the same as
the directory it is in. No special cases for slashes or empty names.
Linus
^ permalink raw reply
* Re: [ANNOUNCE] qgit, another git GUI viewer
From: Stanislav Karchebny @ 2005-05-31 23:48 UTC (permalink / raw)
To: Marco Costalba; +Cc: git
In-Reply-To: <20050531181109.GH11774@osuosl.org>
[-- Attachment #1: Type: text/plain, Size: 803 bytes --]
On Tuesday 31 May 2005 21:11, you wrote:
> Obligatory screenshot:
> http://ifup.org/~philips/qgit.png
This looks like very bad athena widget set. Not qtish =)
> Out of the box it wouldn't compile for several reasons on my own
> computer. I have attached a diff but the only real solution is to use
> autotools as my diff will now only work on Gentoo systems.
If you could, please do not use autotools. qmake or better scons is the way to
go. With scons (even if you distribute together with program) overhead does
not exceed 60kb and i guess everyone has python installed these days (if not
- qmake can handle it just right).
If you need a patch for scons, drop me a mail.
--
Best regards,
Stanislav Karchebny
Skype name: berkus
Get skype for free from www.skype.com
[-- Attachment #2: Type: application/pgp-signature, Size: 189 bytes --]
^ permalink raw reply
* Re: git-rev-list: proper lazy reachability
From: Jon Seymour @ 2005-06-01 0:14 UTC (permalink / raw)
To: Linus Torvalds; +Cc: Git Mailing List
In-Reply-To: <Pine.LNX.4.58.0505310758270.1876@ppc970.osdl.org>
On 6/1/05, Linus Torvalds <torvalds@osdl.org> wrote:
>
>
> On Tue, 31 May 2005, Jon Seymour wrote:
> >
> > A
> > / | \
> > B C D
> > | / /
> > E
> > | \
> > F G
> >
> > and searching for git-rev-list A ^B it would actually be better to stop
> > at E (printing A,B,C,D,E)
>
> Btw, you probably looked at the actual code, so you know this, but maybe
> it wasn't clear to everybody else: the code obviously _will_ look at all
> of ABCDE before it can even decide that it should print out ACD, since it
> really needs to.
I did look at the code and concluded that ACD would be displayed and
simply assumed B would also be displayed. Anyway, you are right, for
the purposes you are interested in neither B nor E are interesting.
>
> But if somebody wanted to actually show this as a _graph_, what you would
> probably want is actually all of ABCDE, except you'd get the "interesting"
> bit separately (ie ACD would be tagged some way). Then you could show a
> sane graph that is colored by whether something is new or not, for
> example. That's absolutely trivial to do wiyth the new rev-list algorithm,
> in case somebody really cares - it's literally just changing the printout
> to show the entries marked "ignored" with some extra marking.
This will also fall pretty naturally out of the --merge-order patch
that I am working on with an appropiate tweak. But I'll shut up about
that until I have code to share...
jon.
^ permalink raw reply
* [PATCH] ls-tree: handle trailing slashes in the pathspec properly.
From: Junio C Hamano @ 2005-06-01 1:46 UTC (permalink / raw)
To: Linus Torvalds; +Cc: Git Mailing List
In-Reply-To: <Pine.LNX.4.58.0505311636260.1876@ppc970.osdl.org>
This fixes the problem with ls-tree which failed to show
"drivers/char" directory when the user asked for "drivers/char/"
from the command line. At the same time, if "drivers/char" were
a non directory, "drivers/char/" would not show it. This is
consistent with the way diffcore-pathspec has been recently
fixed.
This adds back the diffcore-pathspec test,dropped when my
earlier diffcore-pathspec fix was rejected.
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
*** OK, this is the third try (2 1/2nd to be exact). I love how
*** you always turn out to be right ;-). BTW could you limit
*** git-apply --stat output to 79 cols not 80?
ls-tree.c | 94 ++++++++++++++++++++++----------------------
t/t3100-ls-tree-restrict.sh | 39 ++++++++++++++++--
t/t4010-diff-pathspec.sh | 65 ++++++++++++++++++++++++++++++
3 files changed, 146 insertions(+), 52 deletions(-)
diff --git a/ls-tree.c b/ls-tree.c
--- a/ls-tree.c
+++ b/ls-tree.c
@@ -54,54 +54,58 @@ static int prepare_children(struct tree_
return 0;
}
-static struct tree_entry_list *find_entry_0(struct tree_entry_list *elem,
- const char *path,
- const char *path_end)
+static struct tree_entry_list *find_entry(const char *path)
{
- const char *ep;
+ const char *next, *slash;
int len;
+ struct tree_entry_list *elem = &root_entry;
- while (path < path_end) {
- if (prepare_children(elem))
- return NULL;
+ /* Find tree element, descending from root, that
+ * corresponds to the named path, lazily expanding
+ * the tree if possible.
+ */
- /* In elem->tree->entries, find the one that has name
- * that matches what is between path and ep.
+ while (path) {
+ /* The fact we still have path means that the caller
+ * wants us to make sure that elem at this point is a
+ * directory, and possibly descend into it. Even what
+ * is left is just trailing slashes, we loop back to
+ * here, and this call to prepare_children() will
+ * catch elem not being a tree. Nice.
*/
- elem = elem->item.tree->entries;
+ if (prepare_children(elem))
+ return NULL;
- ep = strchr(path, '/');
- if (!ep || path_end <= ep)
- ep = path_end;
- len = ep - path;
-
- while (elem) {
- if ((strlen(elem->name) == len) &&
- !strncmp(elem->name, path, len))
- break;
- elem = elem->next;
+ slash = strchr(path, '/');
+ if (!slash) {
+ len = strlen(path);
+ next = 0;
+ }
+ else {
+ next = slash + 1;
+ len = slash - path;
}
- if (path_end <= ep || !elem)
- return elem;
- while (*ep == '/' && ep < path_end)
- ep++;
- path = ep;
+ if (len) {
+ /* (len == 0) if the original path was "drivers/char/"
+ * and we have run already two rounds, having elem
+ * pointing at the drivers/char directory.
+ */
+ elem = elem->item.tree->entries;
+ while (elem) {
+ if ((strlen(elem->name) == len) &&
+ !strncmp(elem->name, path, len)) {
+ /* found */
+ break;
+ }
+ elem = elem->next;
+ }
+ if (!elem)
+ return NULL;
+ }
+ path = next;
}
- return NULL;
-}
-static struct tree_entry_list *find_entry(const char *path,
- const char *path_end)
-{
- /* Find tree element, descending from root, that
- * corresponds to the named path, lazily expanding
- * the tree if possible.
- */
- if (path == path_end) {
- /* Special. This is the root level */
- return &root_entry;
- }
- return find_entry_0(&root_entry, path, path_end);
+ return elem;
}
static void show_entry_name(struct tree_entry_list *e)
@@ -180,10 +184,10 @@ static int show_entry(struct tree_entry_
return err;
}
-static int list_one(const char *path, const char *path_end)
+static int list_one(const char *path)
{
int err = 0;
- struct tree_entry_list *e = find_entry(path, path_end);
+ struct tree_entry_list *e = find_entry(path);
if (!e) {
/* traditionally ls-tree does not complain about
* missing path. We may change this later to match
@@ -199,12 +203,8 @@ static int list(char **path)
{
int i;
int err = 0;
- for (i = 0; path[i]; i++) {
- int len = strlen(path[i]);
- while (0 <= len && path[i][len] == '/')
- len--;
- err = err | list_one(path[i], path[i] + len);
- }
+ for (i = 0; path[i]; i++)
+ err = err | list_one(path[i]);
return err;
}
diff --git a/t/t3100-ls-tree-restrict.sh b/t/t3100-ls-tree-restrict.sh
--- a/t/t3100-ls-tree-restrict.sh
+++ b/t/t3100-ls-tree-restrict.sh
@@ -14,7 +14,7 @@ This test runs git-ls-tree with the foll
path2/baz/b - a file in a directory in a directory
The new path restriction code should do the right thing for path2 and
-path2/baz
+path2/baz. Also path0/ should snow nothing.
'
. ./test-lib.sh
@@ -63,7 +63,7 @@ EOF
test_output'
test_expect_success \
- 'ls-tree filtered' \
+ 'ls-tree filtered with path' \
'git-ls-tree $tree path >current &&
cat >expected <<\EOF &&
EOF
@@ -71,7 +71,7 @@ EOF
test_expect_success \
- 'ls-tree filtered' \
+ 'ls-tree filtered with path1 path0' \
'git-ls-tree $tree path1 path0 >current &&
cat >expected <<\EOF &&
120000 blob X path1
@@ -80,7 +80,14 @@ EOF
test_output'
test_expect_success \
- 'ls-tree filtered' \
+ 'ls-tree filtered with path0/' \
+ 'git-ls-tree $tree path0/ >current &&
+ cat >expected <<\EOF &&
+EOF
+ test_output'
+
+test_expect_success \
+ 'ls-tree filtered with path2' \
'git-ls-tree $tree path2 >current &&
cat >expected <<\EOF &&
040000 tree X path2
@@ -91,7 +98,7 @@ EOF
test_output'
test_expect_success \
- 'ls-tree filtered' \
+ 'ls-tree filtered with path2/baz' \
'git-ls-tree $tree path2/baz >current &&
cat >expected <<\EOF &&
040000 tree X path2/baz
@@ -99,4 +106,26 @@ test_expect_success \
EOF
test_output'
+test_expect_success \
+ 'ls-tree filtered with path2' \
+ 'git-ls-tree $tree path2 >current &&
+ cat >expected <<\EOF &&
+040000 tree X path2
+040000 tree X path2/baz
+120000 blob X path2/bazbo
+100644 blob X path2/foo
+EOF
+ test_output'
+
+test_expect_success \
+ 'ls-tree filtered with path2/' \
+ 'git-ls-tree $tree path2/ >current &&
+ cat >expected <<\EOF &&
+040000 tree X path2
+040000 tree X path2/baz
+120000 blob X path2/bazbo
+100644 blob X path2/foo
+EOF
+ test_output'
+
test_done
diff --git a/t/t4010-diff-pathspec.sh b/t/t4010-diff-pathspec.sh
new file mode 100644
--- /dev/null
+++ b/t/t4010-diff-pathspec.sh
@@ -0,0 +1,65 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='Pathspec restrictions
+
+Prepare:
+ file0
+ path1/file1
+'
+. ./test-lib.sh
+. ../diff-lib.sh ;# test-lib chdir's into trash
+
+test_expect_success \
+ setup \
+ 'echo frotz >file0 &&
+ mkdir path1 &&
+ echo rezrov >path1/file1 &&
+ git-update-cache --add file0 path1/file1 &&
+ tree=`git-write-tree` &&
+ echo "$tree" &&
+ echo nitfol >file0 &&
+ echo yomin >path1/file1 &&
+ git-update-cache file0 path1/file1'
+
+cat >expected <<\EOF
+EOF
+test_expect_success \
+ 'limit to path should show nothing' \
+ 'git-diff-cache --cached $tree path >current &&
+ compare_diff_raw current expected'
+
+cat >expected <<\EOF
+:100644 100644 766498d93a4b06057a8e49d23f4068f1170ff38f 0a41e115ab61be0328a19b29f18cdcb49338d516 M path1/file1
+EOF
+test_expect_success \
+ 'limit to path1 should show path1/file1' \
+ 'git-diff-cache --cached $tree path1 >current &&
+ compare_diff_raw current expected'
+
+cat >expected <<\EOF
+:100644 100644 766498d93a4b06057a8e49d23f4068f1170ff38f 0a41e115ab61be0328a19b29f18cdcb49338d516 M path1/file1
+EOF
+test_expect_success \
+ 'limit to path1/ should show path1/file1' \
+ 'git-diff-cache --cached $tree path1/ >current &&
+ compare_diff_raw current expected'
+
+cat >expected <<\EOF
+:100644 100644 766498d93a4b06057a8e49d23f4068f1170ff38f 0a41e115ab61be0328a19b29f18cdcb49338d516 M file0
+EOF
+test_expect_success \
+ 'limit to file0 should show file0' \
+ 'git-diff-cache --cached $tree file0 >current &&
+ compare_diff_raw current expected'
+
+cat >expected <<\EOF
+EOF
+test_expect_success \
+ 'limit to file0/ should emit nothing.' \
+ 'git-diff-cache --cached $tree file0/ >current &&
+ compare_diff_raw current expected'
+
+test_done
------------
^ permalink raw reply
* [PATCH] cg-clone fails to clone tags
From: Miguel Bazdresch @ 2005-06-01 1:58 UTC (permalink / raw)
To: git
Hi,
I noticed that cg-clone fails to clone tags when the source and
destination directories are on different filesystems (this is on a local
clone). The command produces warnings of the type:
cp: cannot create link `.git/refs/tags/git-pasky-0.6.2': Invalid cross-device link
`/home/miguel/bin/cogito/.git/refs/tags/git-pasky-0.6.3' -> `.git/refs/tags/git-pasky-0.6.3'
I think the culprit is the "l" flag passed to cp in fetch-local() in
script cg-pull. After thinking a bit on how to solve this, I decided on
a "blunt force" approach, where the cp is done twice, once with "l" and
once without it. Only when both fail is a warning issued about the
failure to get the tag list.
This has the unfortunate side effect of also removing the "u" flag to
cp, but I don't think it's a big deal (cg-pull adds the "l" and "u"
flags at the same time).
Following is the output of cg-mkpatch:
---
Currently, cg-clone fails to clone the tag list if the destination
and source directory are in different filesystem. This caused by the
"l" flag used by fetch_local.
This patch tries the copy without the "l" flag if it fails with it.
Signed-off-by: Miguel Bazdresch <miguelb@ieee.org>
---
commit 5823514635ca67be41914d9294081353b70272a4
tree 19092122a45366f46b7f140d411f875000ff2ba7
parent 20e473c9afd8b5d2d549b0e7881473600beb9c37
author Miguel Bazdresch <miguelb@ieee.org> Tue, 31 May 2005 20:23:44
-0500
committer Miguel Bazdresch <miguelb@ieee.org> Tue, 31 May 2005 20:23:44
-0500
cg-pull | 10 +++++++++-
1 files changed, 9 insertions(+), 1 deletions(-)
diff --git a/cg-pull b/cg-pull
--- a/cg-pull
+++ b/cg-pull
@@ -259,8 +259,16 @@ rsyncerr=
$fetch -i -s -u -d "$uri/refs/tags" "$_git/refs/tags" || rsyncerr=1
if [ "$rsyncerr" ]; then
rsyncerr=
- $fetch -i -s -u -d "$uri/tags" "$_git/refs/tags" || rsyncerr=1
+ $fetch -i -s -d "$uri/refs/tags" "$_git/refs/tags" || rsyncerr=1
fi
+if [ "$rsyncerr" ]; then
+rsyncerr=
+ $fetch -i -s -u -d "$uri/tags" "$_git/refs/tags" || rsyncerr=1
+fi
+if [ "$rsyncerr" ]; then
+rsyncerr=
+ $fetch -i -s -d "$uri/tags" "$_git/refs/tags" || rsyncerr=1
+fi
[ "$rsyncerr" ] && echo "unable to get tags list (non-fatal)" >&2
^ permalink raw reply
* Re: I want to release a "git-1.0"
From: Junio C Hamano @ 2005-06-01 2:11 UTC (permalink / raw)
To: Chris Wedgwood; +Cc: Linus Torvalds, Git Mailing List
In-Reply-To: <Pine.LNX.4.58.0505301801520.1876@ppc970.osdl.org>
>>>>> "LT" == Linus Torvalds <torvalds@osdl.org> writes:
LT> On Mon, 30 May 2005, Chris Wedgwood wrote:
>>
>> I'm still at a loss how to do the equivalent of annotate. I know a
>> couple of front ends can do this but I have no idea what command line
>> magic would be equivalent.
LT> There isn't any. It's actually pretty nasty to do, following history
LT> backwards and keeping track of lines as they are added. I know how, I'm
LT> just really lazy and hoping somebody else will do it, since I really end
LT> up not caring that much myself.
LT> I notice that Thomas Gleixner seems to have one, but that one is based on
LT> a database, and doesn't look usable as a standalone command..
Here is my quick-and-dirty one done in Perl. This is dog-slow
and not suited for interactive use, but its algorithm should
handle the merges, renames and complete rewrites correctly.
Its sample output for:
$ blame.perl HEAD git-commit-script
look like this (I've edited the SHA1 and names to make it a bit
shorter, but it still does not fit on my 80-column terminal X-<).
For each line in the version in the HEAD, it outputs (TAB
separated) the SHA1 of the commit that is responsible for the
line to be there, author, commiter, line number in the version
of the guity commit and the filename in the guilty commit (this
file could have been renamed in which case this may not match
the name of the file the script was originally asked to
annotate). It shows that 9th line was what was in
a3e870f2... commit as 11th line done by Linus, for example.
:a3e870f2... Linus T....osdl.org> Linus T....osdl.org> 1 git-commit-script
:a3e870f2... Linus T....osdl.org> Linus T....osdl.org> 2 git-commit-script
:a3e870f2... Linus T....osdl.org> Linus T....osdl.org> 3 git-commit-script
:a3e870f2... Linus T....osdl.org> Linus T....osdl.org> 4 git-commit-script
:a3e870f2... Linus T....osdl.org> Linus T....osdl.org> 5 git-commit-script
:a3e870f2... Linus T....osdl.org> Linus T....osdl.org> 6 git-commit-script
:a3e870f2... Linus T....osdl.org> Linus T....osdl.org> 7 git-commit-script
:2036d841... Junio C....@cox.net> Linus T....osdl.org> 8 git-commit-script
:a3e870f2... Linus T....osdl.org> Linus T....osdl.org> 11 git-commit-script
:a3e870f2... Linus T....osdl.org> Linus T....osdl.org> 12 git-commit-script
:a3e870f2... Linus T....osdl.org> Linus T....osdl.org> 13 git-commit-script
:a3e870f2... Linus T....osdl.org> Linus T....osdl.org> 14 git-commit-script
:a3e870f2... Linus T....osdl.org> Linus T....osdl.org> 15 git-commit-script
------------
A blame script for use by higher-level annotate tools.
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
diff -u a/blame.perl b/blame.perl
--- /dev/null
+++ b/blame.perl
@@ -0,0 +1,400 @@
+#!/usr/bin/perl -w
+
+use strict;
+
+package main;
+$::debug = 0;
+
+sub read_blob {
+ my $sha1 = shift;
+ my $fh = undef;
+ my $result;
+ local ($/) = undef;
+ open $fh, '-|', 'git-cat-file', 'blob', $sha1
+ or die "cannot read blob $sha1";
+ $result = join('', <$fh>);
+ close $fh
+ or die "failure while closing pipe to git-cat-file";
+ return $result;
+}
+
+sub read_diff_raw {
+ my ($parent, $filename) = @_;
+ my $fh = undef;
+ local ($/) = "\0";
+ my @result = ();
+ my ($meta, $status, $sha1_1, $sha1_2, $file1, $file2);
+ print STDERR "* diff-cache $parent\n" if $::debug;
+ open $fh, '-|', 'git-diff-cache', '-B', '-C', '--cached', '-z', $parent
+ or die "cannot read git-diff-cache with $parent";
+ while (defined ($meta = <$fh>)) {
+ chomp($meta);
+ (undef, undef, $sha1_1, $sha1_2, $status) = split(/ /, $meta);
+ $file1 = <$fh>;
+ chomp($file1);
+ if ($status =~ /^[CR]/) {
+ $file2 = <$fh>;
+ chomp($file2);
+ } elsif ($status =~ /^D/) {
+ next;
+ } else {
+ $file2 = $file1;
+ }
+ if ($file2 eq $filename) {
+ push @result, [$status, $sha1_1, $sha1_2, $file1, $file2];
+ }
+ }
+ close $fh
+ or die "failure while closing pipe to git-diff-cache";
+ return @result;
+}
+
+sub write_temp_blob {
+ my ($sha1, $temp) = @_;
+ my $fh = undef;
+ my $blob = read_blob($sha1);
+ open $fh, '>', $temp
+ or die "cannot open temporary file $temp";
+ print $fh $blob;
+ close($fh);
+}
+
+package Git::Patch;
+sub new {
+ my ($class, $sha1_1, $sha1_2) = @_;
+ my $self = bless [], $class;
+ my $fh = undef;
+ ::write_temp_blob($sha1_1, "/tmp/blame-$$-1");
+ ::write_temp_blob($sha1_2, "/tmp/blame-$$-2");
+ open $fh, '-|', 'diff', '-u0', "/tmp/blame-$$-1", "/tmp/blame-$$-2"
+ or die "cannot read diff";
+ while (<$fh>) {
+ if (/^\@\@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? \@\@/) {
+ push @$self, [$1, (defined $2 ? $2 : 1),
+ $3, (defined $4 ? $4 : 1)];
+ }
+ }
+ close $fh;
+ unlink "/tmp/blame-$$-1", "/tmp/blame-$$-2";
+ return $self;
+}
+
+sub find_parent_line {
+ my ($self, $commit_lineno) = @_;
+ my $ofs = 0;
+ for (@$self) {
+ my ($line_1, $len_1, $line_2, $len_2) = @$_;
+ if ($commit_lineno < $line_2) {
+ return $commit_lineno - $ofs;
+ }
+ if ($line_2 <= $commit_lineno && $commit_lineno < $line_2 + $len_2) {
+ return -1; # changed by commit.
+ }
+ $ofs += ($len_1 - $len_2);
+ }
+ return $commit_lineno + $ofs;
+}
+
+package Git::Commit;
+sub new {
+ my $class = shift;
+ my $self = bless {
+ PARENT => [],
+ TREE => undef,
+ AUTHOR => undef,
+ COMMITTER => undef,
+ }, $class;
+ my $commit_sha1 = shift;
+ $self->{SHA1} = $commit_sha1;
+ my $fh = undef;
+ open $fh, '-|', 'git-cat-file', 'commit', $commit_sha1
+ or die "cannot read commit object $commit_sha1";
+ while (<$fh>) {
+ chomp;
+ if (/^tree ([0-9a-f]{40})$/) { $self->{TREE} = $1; }
+ elsif (/^parent ([0-9a-f]{40})$/) { push @{$self->{PARENT}}, $1; }
+ elsif (/^author ([^>]+>)/) { $self->{AUTHOR} = $1; }
+ elsif (/^committer ([^>]+>)/) { $self->{COMMITTER} = $1; }
+ }
+ close $fh
+ or die "failure while closing pipe to git-cat-file";
+ return $self;
+}
+
+sub find_file {
+ my ($commit, $path) = @_;
+ my $result = undef;
+ my $fh = undef;
+ local ($/) = "\0";
+ open $fh, '-|', 'git-ls-tree', '-z', '-r', '-d', $commit->{TREE}, $path
+ or die "cannot read git-ls-tree $commit->{TREE}";
+ while (<$fh>) {
+ chomp;
+ if (/^[0-7]{6} blob ([0-9a-f]{40}) (.*)$/) {
+ if ($2 ne $path) {
+ die "$2 ne $path???";
+ }
+ $result = $1;
+ last;
+ }
+ }
+ close $fh
+ or die "failure while closing pipe to git-ls-tree";
+ return $result;
+}
+
+package Git::Blame;
+sub new {
+ my $class = shift;
+ my $self = bless {
+ LINE => [],
+ UNKNOWN => undef,
+ WORK => [],
+ }, $class;
+ my $commit = shift;
+ my $filename = shift;
+ my $sha1 = $commit->find_file($filename);
+ my $blob = ::read_blob($sha1);
+ my @blob = (split(/\n/, $blob));
+ for (my $i = 0; $i < @blob; $i++) {
+ $self->{LINE}[$i] = +{
+ COMMIT => $commit,
+ FOUND => undef,
+ FILENAME => $filename,
+ LINENO => ($i + 1),
+ };
+ }
+ $self->{UNKNOWN} = scalar @blob;
+ push @{$self->{WORK}}, [$commit, $filename];
+ return $self;
+}
+
+sub print {
+ my $self = shift;
+ my $line_termination = shift;
+ for (my $i = 0; $i < @{$self->{LINE}}; $i++) {
+ my $l = $self->{LINE}[$i];
+ print ($l->{FOUND} ? ':' : '?');;
+ print "$l->{COMMIT}->{SHA1} ";
+ print "$l->{COMMIT}->{AUTHOR} ";
+ print "$l->{COMMIT}->{COMMITTER} ";
+ print "$l->{LINENO} $l->{FILENAME}";
+ print $line_termination;
+ }
+}
+
+sub take_responsibility {
+ my ($self, $commit) = @_;
+ for (my $i = 0; $i < @{$self->{LINE}}; $i++) {
+ my $l = $self->{LINE}[$i];
+ if (! $l->{FOUND} && ($l->{COMMIT}->{SHA1} eq $commit->{SHA1})) {
+ $l->{FOUND} = 1;
+ $self->{UNKNOWN}--;
+ }
+ }
+}
+
+sub blame_parent {
+ my ($self, $commit, $parent, $filename) = @_;
+ my @diff = ::read_diff_raw($parent->{SHA1}, $filename);
+ my $filename_in_parent;
+ my $passed_blame_to_parent = undef;
+ if (@diff == 0) {
+ # We have not touched anything. Blame parent for everything
+ # that we are suspected for.
+ for (my $i = 0; $i < @{$self->{LINE}}; $i++) {
+ my $l = $self->{LINE}[$i];
+ if (! $l->{FOUND} && ($l->{COMMIT}->{SHA1} eq $commit->{SHA1})) {
+ $l->{COMMIT} = $parent;
+ $passed_blame_to_parent = 1;
+ }
+ }
+ $filename_in_parent = $filename;
+ }
+ elsif (@diff != 1) {
+ # This should not happen.
+ for (@diff) {
+ print "** @$_\n";
+ }
+ die "Oops";
+ }
+ else {
+ my ($status, $sha1_1, $sha1_2, $file1, $file2) = @{$diff[0]};
+ print STDERR "** $status $file1 $file2\n" if $::debug;
+ if ($status =~ /N/) {
+ # Either some of other parents created it, or we did.
+ # At this point the only thing we know is that this
+ # parent is not responsible for it.
+ ;
+ }
+ else {
+ my $patch = Git::Patch->new($sha1_1, $sha1_2);
+ $filename_in_parent = $file1;
+ for (my $i = 0; $i < @{$self->{LINE}}; $i++) {
+ my $l = $self->{LINE}[$i];
+ if (! $l->{FOUND} && $l->{COMMIT}->{SHA1} eq $commit->{SHA1}) {
+ # We are suspected to have introduced this line.
+ # Does it exist in the parent?
+ my $lineno = $l->{LINENO};
+ my $parent_line = $patch->find_parent_line($lineno);
+ if ($parent_line < 0) {
+ # No, we may be the guilty ones, or some other
+ # parent might be. We do not assign blame to
+ # ourselves here yet.
+ ;
+ }
+ else {
+ # This line is coming from the parent, so pass
+ # blame to it.
+ $l->{COMMIT} = $parent;
+ $l->{FILENAME} = $file1;
+ $l->{LINENO} = $parent_line;
+ $passed_blame_to_parent = 1;
+ }
+ }
+ }
+ }
+ }
+ if ($passed_blame_to_parent && $self->{UNKNOWN}) {
+ unshift @{$self->{WORK}},
+ [$parent, $filename_in_parent];
+ }
+}
+
+sub assign {
+ my ($self, $commit, $filename) = @_;
+ # We do read-tree of the current commit and diff-cache
+ # with each parents, instead of running diff-tree. This
+ # is because diff-tree does not look for copies hard enough.
+ #
+ print STDERR "* read-tree $commit->{SHA1}\n" if $::debug;
+ system('git-read-tree', '-m', $commit->{SHA1});
+ for my $parent (@{$commit->{PARENT}}) {
+ $self->blame_parent($commit, Git::Commit->new($parent), $filename);
+ }
+ $self->take_responsibility($commit);
+}
+
+sub assign_blame {
+ my ($self) = @_;
+ while ($self->{UNKNOWN} && @{$self->{WORK}}) {
+ my $wk = shift @{$self->{WORK}};
+ my ($commit, $filename) = @$wk;
+ $self->assign($commit, $filename);
+ }
+}
+
+
+
+################################################################
+package main;
+my $usage = "blame [-z] <commit> filename";
+my $line_termination = "\n";
+
+$::ENV{GIT_INDEX_FILE} = "/tmp/blame-$$-index";
+unlink($::ENV{GIT_INDEX_FILE});
+
+if ($ARGV[0] eq '-z') {
+ $line_termination = "\0";
+ shift;
+}
+
+if (@ARGV != 2) {
+ die $usage;
+}
+
+my $head_commit = Git::Commit->new($ARGV[0]);
+my $filename = $ARGV[1];
+my $blame = Git::Blame->new($head_commit, $filename);
+
+$blame->assign_blame();
+$blame->print($line_termination);
+
+unlink($::ENV{GIT_INDEX_FILE});
+
+__END__
+
+How does this work, and what do we do about merges?
+
+The algorithm considers that the first parent is our main line of
+development and treats it somewhat special than other parents. So we
+pass on the blame to the first parent if a line has not changed from
+it. For lines that have changed from the first parent, we must have
+either inherited that change from some other parent, or it could have
+been merge conflict resolution edit we did on our own.
+
+The following picture illustrates how we pass on and assign blames.
+
+In the sample, the original O was forked into A and B and then merged
+into M. Line 1, 2, and 4 did not change. Line 3 and 5 are changed in
+A, and Line 5 and 6 are changed in B. M made its own decision to
+resolve merge conflicts at Line 5 to something different from A and B:
+
+ A: 1 2 T 4 T 6
+ / \
+O: 1 2 3 4 5 6 M: 1 2 T 4 M S
+ \ /
+ B: 1 2 3 4 S S
+
+In the following picture, each line is annotated with a blame letter.
+A lowercase blame (e.g. "a" for "1") means that commit or its ancestor
+is the guilty party but we do not know which particular ancestor is
+responsible for the change yet. An uppercase blame means that we know
+that commit is the guilty party.
+
+First we look at M (the HEAD) and initialize Git::Blame->{LINE} like
+this:
+
+ M: 1 2 T 4 M S
+ m m m m m m
+
+That is, we know all lines are results of modification made by some
+ancestor of M, so we assign lowercase 'm' to all of them.
+
+Then we examine our first parent A. Throughout the algorithm, we are
+always only interested in the lines we are the suspect, but this being
+the initial round, we are the suspect for all of them. We notice that
+1 2 T 4 are the same as the parent A, so we pass the blame for these
+four lines to A. M and S are different from A, so we leave them as
+they are (note that we do not immediately take the blame for them):
+
+ M: 1 2 T 4 M S
+ a a a a m m
+
+Next we go on to examine parent B. Again, we are only interested in
+the lines we are still the suspect (i.e. M and S). We notice S is
+something we inherited from B, so we pass the blame on to it, like
+this:
+
+ M: 1 2 T 4 M S
+ a a a a m b
+
+Once we exhausted the parents, we look at the results and take
+responsibility for the remaining ones that we are still the suspect:
+
+ M: 1 2 T 4 M S
+ a a a a M b
+
+We are done with M. And we know commits A and B need to be examined
+further, so we do them recursively. When we look at A, we again only
+look at the lines that A is the suspect:
+
+ A: 1 2 T 4 T 6
+ a a a a M b
+
+Among 1 2 T 4, comparing against its parent O, we notice 1 2 4 are
+the same so pass the blame for those lines to O:
+
+ A: 1 2 T 4 T 6
+ o o a o M b
+
+A is a non-merge commit; we have already exhausted the parents and
+take responsibility for the remaining ones that A is the suspect:
+
+ A: 1 2 T 4 T 6
+ o o A o M b
+
+We go on like this and the final result would become:
+
+ O: 1 2 3 4 5 6
+ O O A O M B
^ permalink raw reply
* Re: I want to release a "git-1.0"
From: David Lang @ 2005-06-01 2:25 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Chris Wedgwood, Linus Torvalds, Git Mailing List
In-Reply-To: <7vu0kiu8pm.fsf@assigned-by-dhcp.cox.net>
On Tue, 31 May 2005, Junio C Hamano wrote:
>>>>>> "LT" == Linus Torvalds <torvalds@osdl.org> writes:
>
> LT> On Mon, 30 May 2005, Chris Wedgwood wrote:
>>>
>>> I'm still at a loss how to do the equivalent of annotate. I know a
>>> couple of front ends can do this but I have no idea what command line
>>> magic would be equivalent.
>
> LT> There isn't any. It's actually pretty nasty to do, following history
> LT> backwards and keeping track of lines as they are added. I know how, I'm
> LT> just really lazy and hoping somebody else will do it, since I really end
> LT> up not caring that much myself.
>
> LT> I notice that Thomas Gleixner seems to have one, but that one is based on
> LT> a database, and doesn't look usable as a standalone command..
>
> Here is my quick-and-dirty one done in Perl. This is dog-slow
> and not suited for interactive use, but its algorithm should
> handle the merges, renames and complete rewrites correctly.
Hmm, thinking out loud. would it help to look at the deltify scripts and
let them find the major chunks and then look in detail only when that
fails?
David Lang
^ permalink raw reply
* Re: I want to release a "git-1.0"
From: Linus Torvalds @ 2005-06-01 3:04 UTC (permalink / raw)
To: Eric W. Biederman; +Cc: Git Mailing List
In-Reply-To: <m1psv7bjb6.fsf@ebiederm.dsl.xmission.com>
On Tue, 31 May 2005, Eric W. Biederman wrote:
>
> I way behind the power curve on learning git at this point but
> one piece of the puzzle that CVS has that I don't believe git does
> are multiple people committing to the same repository, especially
> remotely. I don't see that as a down side of git but it is a common
> way people CVS so it is worth documenting.
It's actually one thing git doesn't do per se.
You have to do a "git-pull-script" from the common repository side,
there's no "git-push-script". Ugly.
Anyway, I wrote just a _very_ introductory thing in
Documentation/tutorial.txt, I'll try to update and expand on it later. It
basically has a really stupid example of "how to set up a new project".
Linus
^ permalink raw reply
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