From a858c006fae17cef44dd63737771f2bebb6eeae8 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 25 Feb 2007 13:13:17 -0800 Subject: [PATCH 01/90] git-fetch: add --quiet Pass it to underlying fetch-pack, and also have it affect if -v is passed to http-fetch and rsync. Signed-off-by: Junio C Hamano --- git-fetch.sh | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/git-fetch.sh b/git-fetch.sh index 5ae0d28cc0..3aa117e321 100755 --- a/git-fetch.sh +++ b/git-fetch.sh @@ -26,6 +26,7 @@ keep= shallow_depth= no_progress= test -t 1 || no_progress=--no-progress +quiet= while case "$#" in 0) break ;; esac do case "$1" in @@ -56,6 +57,9 @@ do --update-head-o|--update-head-ok) update_head_ok=t ;; + -q|--q|--qu|--qui|--quie|--quiet) + quiet=--quiet + ;; -v|--verbose) verbose=Yes ;; @@ -337,7 +341,8 @@ fetch_main () { expr "z$head" : "z$_x40\$" >/dev/null || die "No such ref $remote_name at $remote" echo >&2 "Fetching $remote_name from $remote using $proto" - git-http-fetch -v -a "$head" "$remote/" || exit + case "$quiet" in '') v=-v ;; *) v= ;; esac + git-http-fetch $v -a "$head" "$remote/" || exit ;; rsync://*) test -n "$shallow_depth" && @@ -346,8 +351,9 @@ fetch_main () { rsync -L -q "$remote/$remote_name" "$TMP_HEAD" || exit 1 head=$(git-rev-parse --verify TMP_HEAD) rm -f "$TMP_HEAD" + case "$quiet" in '') v=-v ;; *) v= ;; esac test "$rsync_slurped_objects" || { - rsync -av --ignore-existing --exclude info \ + rsync -a $v --ignore-existing --exclude info \ "$remote/objects/" "$GIT_OBJECT_DIRECTORY/" || exit # Look at objects/info/alternates for rsync -- http will @@ -394,8 +400,8 @@ fetch_main () { git-bundle unbundle "$remote" $rref || echo failed "$remote" else - git-fetch-pack --thin $exec $keep $shallow_depth $no_progress \ - "$remote" $rref || + git-fetch-pack --thin $exec $keep $shallow_depth \ + $quiet $no_progress "$remote" $rref || echo failed "$remote" fi ) | From d59c4b6fb731e5fddb458106b2e18eed1087c507 Mon Sep 17 00:00:00 2001 From: Eygene Ryabinkin Date: Tue, 27 Mar 2007 14:36:12 +0400 Subject: [PATCH 02/90] [PATCH] Teach gitk to use the user-defined UI font everywhere. Some parts of gitk were not respecting the default GUI font. Most of them were catched and fixed. Signed-off-by: Eygene Ryabinkin Signed-off-by: Paul Mackerras --- gitk | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/gitk b/gitk index db28d745dc..d47d3d8f84 100755 --- a/gitk +++ b/gitk @@ -648,8 +648,10 @@ proc makewindow {} { frame .bright.mode radiobutton .bright.mode.patch -text "Patch" \ -command reselectline -variable cmitmode -value "patch" + .bright.mode.patch configure -font $uifont radiobutton .bright.mode.tree -text "Tree" \ -command reselectline -variable cmitmode -value "tree" + .bright.mode.tree configure -font $uifont grid .bright.mode.patch .bright.mode.tree -sticky ew pack .bright.mode -side top -fill x set cflist .bright.cfiles @@ -922,6 +924,7 @@ proc bindall {event action} { } proc about {} { + global uifont set w .about if {[winfo exists $w]} { raise $w @@ -937,11 +940,14 @@ Copyright Use and redistribute under the terms of the GNU General Public License} \ -justify center -aspect 400 pack $w.m -side top -fill x -padx 20 -pady 20 + $w.m configure -font $uifont button $w.ok -text Close -command "destroy $w" pack $w.ok -side bottom + $w.ok configure -font $uifont } proc keys {} { + global uifont set w .keys if {[winfo exists $w]} { raise $w @@ -990,8 +996,10 @@ f Scroll diff view to next file } \ -justify left -bg white -border 2 -relief sunken pack $w.m -side top -fill both + $w.m configure -font $uifont button $w.ok -text Close -command "destroy $w" pack $w.ok -side bottom + $w.ok configure -font $uifont } # Procedures for manipulating the file list window at the @@ -1457,20 +1465,21 @@ proc vieweditor {top n title} { toplevel $top wm title $top $title label $top.nl -text "Name" -font $uifont - entry $top.name -width 20 -textvariable newviewname($n) + entry $top.name -width 20 -textvariable newviewname($n) -font $uifont grid $top.nl $top.name -sticky w -pady 5 - checkbutton $top.perm -text "Remember this view" -variable newviewperm($n) + checkbutton $top.perm -text "Remember this view" -variable newviewperm($n) \ + -font $uifont grid $top.perm - -pady 5 -sticky w message $top.al -aspect 1000 -font $uifont \ -text "Commits to include (arguments to git rev-list):" grid $top.al - -sticky w -pady 5 entry $top.args -width 50 -textvariable newviewargs($n) \ - -background white + -background white -font $uifont grid $top.args - -sticky ew -padx 5 message $top.l -aspect 1000 -font $uifont \ -text "Enter files and directories to include, one per line:" grid $top.l - -sticky w - text $top.t -width 40 -height 10 -background white + text $top.t -width 40 -height 10 -background white -font $uifont if {[info exists viewfiles($n)]} { foreach f $viewfiles($n) { $top.t insert end $f @@ -1481,8 +1490,10 @@ proc vieweditor {top n title} { } grid $top.t - -sticky ew -padx 5 frame $top.buts - button $top.buts.ok -text "OK" -command [list newviewok $top $n] - button $top.buts.can -text "Cancel" -command [list destroy $top] + button $top.buts.ok -text "OK" -command [list newviewok $top $n] \ + -font $uifont + button $top.buts.can -text "Cancel" -command [list destroy $top] \ + -font $uifont grid $top.buts.ok $top.buts.can grid columnconfigure $top.buts 0 -weight 1 -uniform a grid columnconfigure $top.buts 1 -weight 1 -uniform a @@ -5813,6 +5824,7 @@ proc doprefs {} { global maxwidth maxgraphpct diffopts global oldprefs prefstop showneartags global bgcolor fgcolor ctext diffcolors + global uifont set top .gitkprefs set prefstop $top @@ -5826,6 +5838,7 @@ proc doprefs {} { toplevel $top wm title $top "Gitk preferences" label $top.ldisp -text "Commit list display options" + $top.ldisp configure -font $uifont grid $top.ldisp - -sticky w -pady 10 label $top.spacer -text " " label $top.maxwidthl -text "Maximum graph width (lines)" \ @@ -5838,6 +5851,7 @@ proc doprefs {} { grid x $top.maxpctl $top.maxpct -sticky w label $top.ddisp -text "Diff display options" + $top.ddisp configure -font $uifont grid $top.ddisp - -sticky w -pady 10 label $top.diffoptl -text "Options for diff program" \ -font optionfont @@ -5850,6 +5864,7 @@ proc doprefs {} { grid x $top.ntag -sticky w label $top.cdisp -text "Colors: press to choose" + $top.cdisp configure -font $uifont grid $top.cdisp - -sticky w -pady 10 label $top.bg -padx 40 -relief sunk -background $bgcolor button $top.bgbut -text "Background" -font optionfont \ @@ -5878,7 +5893,9 @@ proc doprefs {} { frame $top.buts button $top.buts.ok -text "OK" -command prefsok + $top.buts.ok configure -font $uifont button $top.buts.can -text "Cancel" -command prefscan + $top.buts.can configure -font $uifont grid $top.buts.ok $top.buts.can grid columnconfigure $top.buts 0 -weight 1 -uniform a grid columnconfigure $top.buts 1 -weight 1 -uniform a From 3a950e9a9cfc2eb4e30cf6e8658dab25196d746e Mon Sep 17 00:00:00 2001 From: Eygene Ryabinkin Date: Tue, 27 Mar 2007 14:36:59 +0400 Subject: [PATCH 03/90] [PATCH] Improve look-and-feel of the gitk tool. Made the default buttons on the dialog active and focused upon the dialog appearence. Bound 'Escape' and 'Return' keys to the dialog dismissal where it was appropriate: mainly for dialogs with only one button and no editable fields. Unified the look of the "About gitk" and "Key bindings" dialogs. Signed-off-by: Eygene Ryabinkin Signed-off-by: Paul Mackerras --- gitk | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/gitk b/gitk index d47d3d8f84..b1c65d7680 100755 --- a/gitk +++ b/gitk @@ -938,12 +938,15 @@ Gitk - a commit viewer for git Copyright © 2005-2006 Paul Mackerras Use and redistribute under the terms of the GNU General Public License} \ - -justify center -aspect 400 - pack $w.m -side top -fill x -padx 20 -pady 20 + -justify center -aspect 400 -border 2 -bg white -relief groove + pack $w.m -side top -fill x -padx 2 -pady 2 $w.m configure -font $uifont - button $w.ok -text Close -command "destroy $w" + button $w.ok -text Close -command "destroy $w" -default active pack $w.ok -side bottom $w.ok configure -font $uifont + bind $w "focus $w.ok" + bind $w "destroy $w" + bind $w "destroy $w" } proc keys {} { @@ -994,12 +997,15 @@ f Scroll diff view to next file Decrease font size Update } \ - -justify left -bg white -border 2 -relief sunken - pack $w.m -side top -fill both + -justify left -bg white -border 2 -relief groove + pack $w.m -side top -fill both -padx 2 -pady 2 $w.m configure -font $uifont - button $w.ok -text Close -command "destroy $w" + button $w.ok -text Close -command "destroy $w" -default active pack $w.ok -side bottom $w.ok configure -font $uifont + bind $w "focus $w.ok" + bind $w "destroy $w" + bind $w "destroy $w" } # Procedures for manipulating the file list window at the @@ -5892,14 +5898,15 @@ proc doprefs {} { grid x $top.hunksepbut $top.hunksep -sticky w frame $top.buts - button $top.buts.ok -text "OK" -command prefsok + button $top.buts.ok -text "OK" -command prefsok -default active $top.buts.ok configure -font $uifont - button $top.buts.can -text "Cancel" -command prefscan + button $top.buts.can -text "Cancel" -command prefscan -default normal $top.buts.can configure -font $uifont grid $top.buts.ok $top.buts.can grid columnconfigure $top.buts 0 -weight 1 -uniform a grid columnconfigure $top.buts 1 -weight 1 -uniform a grid $top.buts - - -pady 10 -sticky ew + bind $top "focus $top.buts.ok" } proc choosecolor {v vi w x cmd} { From 3e0318a3613ae8e89dcb1fc39d909145e64287b9 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 29 Mar 2007 01:02:50 -0700 Subject: [PATCH 04/90] checkout: allow detaching to HEAD even when switching to the tip of a branch You cannot currently checkout the tip of an existing branch without moving to the branch. This allows you to detach your HEAD and place it at such a commit, with: $ git checkout master^0 Signed-off-by: Junio C Hamano --- git-checkout.sh | 4 ++-- t/t7201-co.sh | 63 ++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 64 insertions(+), 3 deletions(-) diff --git a/git-checkout.sh b/git-checkout.sh index a7390e808c..deb0a9a3c7 100755 --- a/git-checkout.sh +++ b/git-checkout.sh @@ -170,7 +170,7 @@ describe_detached_head () { } } -if test -z "$branch$newbranch" && test "$new" != "$old" +if test -z "$branch$newbranch" && test "$new_name" != "$old_name" then detached="$new" if test -n "$oldbranch" && test -z "$quiet" @@ -180,7 +180,7 @@ If you want to create a new branch from this checkout, you may do so (now or later) by using -b with the checkout command again. Example: git checkout -b " fi -elif test -z "$oldbranch" +elif test -z "$oldbranch" && test "$new" != "$old" then describe_detached_head 'Previous HEAD position was' "$old" fi diff --git a/t/t7201-co.sh b/t/t7201-co.sh index 867bbd26cb..5fa6a45577 100755 --- a/t/t7201-co.sh +++ b/t/t7201-co.sh @@ -3,7 +3,20 @@ # Copyright (c) 2006 Junio C Hamano # -test_description='git-checkout tests.' +test_description='git-checkout tests. + +Creates master, forks renamer and side branches from it. +Test switching across them. + + ! [master] Initial A one, A two + * [renamer] Renamer R one->uno, M two + ! [side] Side M one, D two, A three + --- + + [side] Side M one, D two, A three + * [renamer] Renamer R one->uno, M two + +*+ [master] Initial A one, A two + +' . ./test-lib.sh @@ -129,4 +142,52 @@ test_expect_success 'checkout -m with merge conflict' ' ! test -s current ' +test_expect_success 'checkout to detach HEAD' ' + + git checkout -f renamer && git clean && + git checkout renamer^ && + H=$(git rev-parse --verify HEAD) && + M=$(git show-ref -s --verify refs/heads/master) && + test "z$H" = "z$M" && + if git symbolic-ref HEAD >/dev/null 2>&1 + then + echo "OOPS, HEAD is still symbolic???" + false + else + : happy + fi +' + +test_expect_success 'checkout to detach HEAD with branchname^' ' + + git checkout -f master && git clean && + git checkout renamer^ && + H=$(git rev-parse --verify HEAD) && + M=$(git show-ref -s --verify refs/heads/master) && + test "z$H" = "z$M" && + if git symbolic-ref HEAD >/dev/null 2>&1 + then + echo "OOPS, HEAD is still symbolic???" + false + else + : happy + fi +' + +test_expect_success 'checkout to detach HEAD with HEAD^0' ' + + git checkout -f master && git clean && + git checkout HEAD^0 && + H=$(git rev-parse --verify HEAD) && + M=$(git show-ref -s --verify refs/heads/master) && + test "z$H" = "z$M" && + if git symbolic-ref HEAD >/dev/null 2>&1 + then + echo "OOPS, HEAD is still symbolic???" + false + else + : happy + fi +' + test_done From 30ca07a249744e57163c02250fca420cea364299 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 31 Mar 2007 23:09:02 -0700 Subject: [PATCH 05/90] _GIT_INDEX_OUTPUT: allow plumbing to output to an alternative index file. When defined, this allows plumbing commands that update the index (add, apply, checkout-index, merge-recursive, mv, read-tree, rm, update-index, and write-tree) to write their resulting index to an alternative index file while holding a lock to the original index file. With this, git-commit that jumps the index does not have to make an extra copy of the index file, and more importantly, it can do the update while holding the lock on the index. However, I think the interface to let an environment variable specify the output is a mistake, as shown in the documentation. If a curious user has the environment variable set to something other than the file GIT_INDEX_FILE points at, almost everything will break. This should instead be a command line parameter to tell these plumbing commands to write the result in the named file, to prevent stupid mistakes. Signed-off-by: Junio C Hamano --- Documentation/git.txt | 8 ++++++++ builtin-add.c | 4 ++-- builtin-apply.c | 6 +++--- builtin-checkout-index.c | 7 ++----- builtin-mv.c | 4 ++-- builtin-read-tree.c | 4 ++-- builtin-rm.c | 4 ++-- builtin-update-index.c | 4 ++-- builtin-write-tree.c | 2 +- cache.h | 5 +++++ git-commit.sh | 4 ++-- lockfile.c | 17 +++++++++++++++++ merge-recursive.c | 4 ++-- 13 files changed, 50 insertions(+), 23 deletions(-) diff --git a/Documentation/git.txt b/Documentation/git.txt index 9defc33273..8fa1a0a588 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -315,6 +315,14 @@ git so take care if using Cogito etc. index file. If not specified, the default of `$GIT_DIR/index` is used. +'_GIT_INDEX_OUTPUT':: + When this environment is defined, plumbing level + commands that update the index writes the resulting + index to this file, instead of `$GIT_INDEX_FILE` (or its + default `$GIT_DIR/index`). This is solely meant to be + used by Porcelain to drive low-level plumbing. Defining + this in user's environment is always an error. + 'GIT_OBJECT_DIRECTORY':: If the object storage directory is specified via this environment variable then the sha1 directories are created diff --git a/builtin-add.c b/builtin-add.c index 9fcf514dbc..54422d84a7 100644 --- a/builtin-add.c +++ b/builtin-add.c @@ -133,7 +133,7 @@ int cmd_add(int argc, const char **argv, const char *prefix) git_config(git_add_config); - newfd = hold_lock_file_for_update(&lock_file, get_index_file(), 1); + newfd = hold_locked_index(&lock_file, 1); for (i = 1; i < argc; i++) { const char *arg = argv[i]; @@ -209,7 +209,7 @@ int cmd_add(int argc, const char **argv, const char *prefix) if (active_cache_changed) { if (write_cache(newfd, active_cache, active_nr) || - close(newfd) || commit_lock_file(&lock_file)) + close(newfd) || commit_locked_index(&lock_file)) die("Unable to write new index file"); } diff --git a/builtin-apply.c b/builtin-apply.c index 27a182bfaa..12011c1c9e 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -2664,8 +2664,8 @@ static int apply_patch(int fd, const char *filename, int inaccurate_eof) write_index = check_index && apply; if (write_index && newfd < 0) - newfd = hold_lock_file_for_update(&lock_file, - get_index_file(), 1); + newfd = hold_locked_index(&lock_file, 1); + if (check_index) { if (read_cache() < 0) die("unable to read index file"); @@ -2872,7 +2872,7 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix) if (write_index) { if (write_cache(newfd, active_cache, active_nr) || - close(newfd) || commit_lock_file(&lock_file)) + close(newfd) || commit_locked_index(&lock_file)) die("Unable to write new index file"); } diff --git a/builtin-checkout-index.c b/builtin-checkout-index.c index afe4b0e452..8460f97b66 100644 --- a/builtin-checkout-index.c +++ b/builtin-checkout-index.c @@ -202,10 +202,7 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix) if (!strcmp(arg, "-u") || !strcmp(arg, "--index")) { state.refresh_cache = 1; if (newfd < 0) - newfd = hold_lock_file_for_update - (&lock_file, get_index_file(), 1); - if (newfd < 0) - die("cannot open index.lock file."); + newfd = hold_locked_index(&lock_file, 1); continue; } if (!strcmp(arg, "-z")) { @@ -302,7 +299,7 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix) if (0 <= newfd && (write_cache(newfd, active_cache, active_nr) || - close(newfd) || commit_lock_file(&lock_file))) + close(newfd) || commit_locked_index(&lock_file))) die("Unable to write new index file"); return 0; } diff --git a/builtin-mv.c b/builtin-mv.c index 737af350b8..820aca122e 100644 --- a/builtin-mv.c +++ b/builtin-mv.c @@ -77,7 +77,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix) git_config(git_default_config); - newfd = hold_lock_file_for_update(&lock_file, get_index_file(), 1); + newfd = hold_locked_index(&lock_file, 1); if (read_cache() < 0) die("index file corrupt"); @@ -285,7 +285,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix) if (active_cache_changed) { if (write_cache(newfd, active_cache, active_nr) || close(newfd) || - commit_lock_file(&lock_file)) + commit_locked_index(&lock_file)) die("Unable to write new index file"); } } diff --git a/builtin-read-tree.c b/builtin-read-tree.c index 793eae0a5f..87048f82ee 100644 --- a/builtin-read-tree.c +++ b/builtin-read-tree.c @@ -100,7 +100,7 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix) setup_git_directory(); git_config(git_default_config); - newfd = hold_lock_file_for_update(&lock_file, get_index_file(), 1); + newfd = hold_locked_index(&lock_file, 1); git_config(git_default_config); @@ -267,7 +267,7 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix) } if (write_cache(newfd, active_cache, active_nr) || - close(newfd) || commit_lock_file(&lock_file)) + close(newfd) || commit_locked_index(&lock_file)) die("unable to write new index file"); return 0; } diff --git a/builtin-rm.c b/builtin-rm.c index bf42003a7e..8a0738f83d 100644 --- a/builtin-rm.c +++ b/builtin-rm.c @@ -110,7 +110,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix) git_config(git_default_config); - newfd = hold_lock_file_for_update(&lock_file, get_index_file(), 1); + newfd = hold_locked_index(&lock_file, 1); if (read_cache() < 0) die("index file corrupt"); @@ -220,7 +220,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix) if (active_cache_changed) { if (write_cache(newfd, active_cache, active_nr) || - close(newfd) || commit_lock_file(&lock_file)) + close(newfd) || commit_locked_index(&lock_file)) die("Unable to write new index file"); } diff --git a/builtin-update-index.c b/builtin-update-index.c index 71cef633c0..84993d7c52 100644 --- a/builtin-update-index.c +++ b/builtin-update-index.c @@ -495,7 +495,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix) /* We can't free this memory, it becomes part of a linked list parsed atexit() */ lock_file = xcalloc(1, sizeof(struct lock_file)); - newfd = hold_lock_file_for_update(lock_file, get_index_file(), 0); + newfd = hold_locked_index(lock_file, 0); if (newfd < 0) lock_error = errno; @@ -661,7 +661,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix) get_index_file(), strerror(lock_error)); } if (write_cache(newfd, active_cache, active_nr) || - close(newfd) || commit_lock_file(lock_file)) + close(newfd) || commit_locked_index(lock_file)) die("Unable to write new index file"); } diff --git a/builtin-write-tree.c b/builtin-write-tree.c index 90fc1cfcf4..c88bbd1b9b 100644 --- a/builtin-write-tree.c +++ b/builtin-write-tree.c @@ -18,7 +18,7 @@ int write_tree(unsigned char *sha1, int missing_ok, const char *prefix) /* We can't free this memory, it becomes part of a linked list parsed atexit() */ struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file)); - newfd = hold_lock_file_for_update(lock_file, get_index_file(), 0); + newfd = hold_locked_index(lock_file, 1); entries = read_cache(); if (entries < 0) diff --git a/cache.h b/cache.h index 384b260227..59a05c1a45 100644 --- a/cache.h +++ b/cache.h @@ -147,6 +147,7 @@ enum object_type { #define DEFAULT_GIT_DIR_ENVIRONMENT ".git" #define DB_ENVIRONMENT "GIT_OBJECT_DIRECTORY" #define INDEX_ENVIRONMENT "GIT_INDEX_FILE" +#define INDEX_OUTPUT_ENVIRONMENT "_GIT_INDEX_OUTPUT" #define GRAFT_ENVIRONMENT "GIT_GRAFT_FILE" #define TEMPLATE_DIR_ENVIRONMENT "GIT_TEMPLATE_DIR" #define CONFIG_ENVIRONMENT "GIT_CONFIG" @@ -212,6 +213,10 @@ struct lock_file { }; extern int hold_lock_file_for_update(struct lock_file *, const char *path, int); extern int commit_lock_file(struct lock_file *); + +extern int hold_locked_index(struct lock_file *, int); +extern int commit_locked_index(struct lock_file *); + extern void rollback_lock_file(struct lock_file *); extern int delete_ref(const char *, unsigned char *sha1); diff --git a/git-commit.sh b/git-commit.sh index 292cf967e3..20c0dc806f 100755 --- a/git-commit.sh +++ b/git-commit.sh @@ -370,8 +370,8 @@ t,) # the same way. if test -z "$initial_commit" then - cp "$THIS_INDEX" "$TMP_INDEX" - GIT_INDEX_FILE="$TMP_INDEX" git-read-tree -i -m HEAD + _GIT_INDEX_OUTPUT="$TMP_INDEX" \ + GIT_INDEX_FILE="$THIS_INDEX" git-read-tree -i -m HEAD else rm -f "$TMP_INDEX" fi || exit diff --git a/lockfile.c b/lockfile.c index 4824f4dc02..2023ebb6ff 100644 --- a/lockfile.c +++ b/lockfile.c @@ -65,6 +65,23 @@ int commit_lock_file(struct lock_file *lk) return i; } +int hold_locked_index(struct lock_file *lk, int die_on_error) +{ + return hold_lock_file_for_update(lk, get_index_file(), die_on_error); +} + +int commit_locked_index(struct lock_file *lk) +{ + char *output = getenv(INDEX_OUTPUT_ENVIRONMENT); + if (output && *output) { + int result = rename(lk->filename, output); + lk->filename[0] = 0; + return result; + } + else + return commit_lock_file(lk); +} + void rollback_lock_file(struct lock_file *lk) { if (lk->filename[0]) diff --git a/merge-recursive.c b/merge-recursive.c index e1aebd7727..f46aaae858 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -1378,7 +1378,7 @@ int main(int argc, char *argv[]) if (show(3)) printf("Merging %s with %s\n", branch1, branch2); - index_fd = hold_lock_file_for_update(lock, get_index_file(), 1); + index_fd = hold_locked_index(lock, 1); for (i = 0; i < bases_count; i++) { struct commit *ancestor = get_ref(bases[i]); @@ -1388,7 +1388,7 @@ int main(int argc, char *argv[]) if (active_cache_changed && (write_cache(index_fd, active_cache, active_nr) || - close(index_fd) || commit_lock_file(lock))) + close(index_fd) || commit_locked_index(lock))) die ("unable to write %s", get_index_file()); return clean ? 0: 1; From 5e7f56ac33f7a5583f9fa4e0b6088709fea7a6f8 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 31 Mar 2007 23:27:41 -0700 Subject: [PATCH 06/90] git-read-tree --index-output= This corrects the interface mistake of the previous one, and gives a command line parameter to the only plumbing command that currently needs it: "git-read-tree". We can add the calls to set_alternate_index_output() to other plumbing commands that update the index if/when needed. Signed-off-by: Junio C Hamano --- Documentation/git-read-tree.txt | 14 +++++++++++++- Documentation/git.txt | 8 -------- builtin-read-tree.c | 7 ++++++- cache.h | 2 +- git-commit.sh | 4 ++-- lockfile.c | 11 ++++++++--- 6 files changed, 30 insertions(+), 16 deletions(-) diff --git a/Documentation/git-read-tree.txt b/Documentation/git-read-tree.txt index 0ff2890c7f..019c8bef7a 100644 --- a/Documentation/git-read-tree.txt +++ b/Documentation/git-read-tree.txt @@ -8,7 +8,7 @@ git-read-tree - Reads tree information into the index SYNOPSIS -------- -'git-read-tree' ( | [[-m [--aggressive] | --reset | --prefix=] [-u | -i]] [--exclude-per-directory=] [ []]) +'git-read-tree' ( | [[-m [--aggressive] | --reset | --prefix=] [-u | -i]] [--exclude-per-directory=] [--index-output=] [ []]) DESCRIPTION @@ -86,6 +86,18 @@ OPTIONS file (usually '.gitignore') and allows such an untracked but explicitly ignored file to be overwritten. +--index-output=:: + Instead of writing the results out to `$GIT_INDEX_FILE`, + write the resulting index in the named file. While the + command is operating, the original index file is locked + with the same mechanism as usual. The file must allow + to be rename(2)ed into from a temporary file that is + created next to the usual index file; typically this + means it needs to be on the same filesystem as the index + file itself, and you need write permission to the + directories the index file and index output file are + located in. + :: The id of the tree object(s) to be read/merged. diff --git a/Documentation/git.txt b/Documentation/git.txt index 8fa1a0a588..9defc33273 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -315,14 +315,6 @@ git so take care if using Cogito etc. index file. If not specified, the default of `$GIT_DIR/index` is used. -'_GIT_INDEX_OUTPUT':: - When this environment is defined, plumbing level - commands that update the index writes the resulting - index to this file, instead of `$GIT_INDEX_FILE` (or its - default `$GIT_DIR/index`). This is solely meant to be - used by Porcelain to drive low-level plumbing. Defining - this in user's environment is always an error. - 'GIT_OBJECT_DIRECTORY':: If the object storage directory is specified via this environment variable then the sha1 directories are created diff --git a/builtin-read-tree.c b/builtin-read-tree.c index 87048f82ee..213bd93c7f 100644 --- a/builtin-read-tree.c +++ b/builtin-read-tree.c @@ -84,7 +84,7 @@ static void prime_cache_tree(void) } -static const char read_tree_usage[] = "git-read-tree ( | [[-m [--aggressive] | --reset | --prefix=] [-u | -i]] [--exclude-per-directory=] [ []])"; +static const char read_tree_usage[] = "git-read-tree ( | [[-m [--aggressive] | --reset | --prefix=] [-u | -i]] [--exclude-per-directory=] [--index-output=] [ []])"; static struct lock_file lock_file; @@ -128,6 +128,11 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix) continue; } + if (!prefixcmp(arg, "--index-output=")) { + set_alternate_index_output(arg + 15); + continue; + } + /* "--prefix=/" means keep the current index * entries and put the entries from the tree under the * given subdirectory. diff --git a/cache.h b/cache.h index 59a05c1a45..592331f706 100644 --- a/cache.h +++ b/cache.h @@ -147,7 +147,6 @@ enum object_type { #define DEFAULT_GIT_DIR_ENVIRONMENT ".git" #define DB_ENVIRONMENT "GIT_OBJECT_DIRECTORY" #define INDEX_ENVIRONMENT "GIT_INDEX_FILE" -#define INDEX_OUTPUT_ENVIRONMENT "_GIT_INDEX_OUTPUT" #define GRAFT_ENVIRONMENT "GIT_GRAFT_FILE" #define TEMPLATE_DIR_ENVIRONMENT "GIT_TEMPLATE_DIR" #define CONFIG_ENVIRONMENT "GIT_CONFIG" @@ -216,6 +215,7 @@ extern int commit_lock_file(struct lock_file *); extern int hold_locked_index(struct lock_file *, int); extern int commit_locked_index(struct lock_file *); +extern void set_alternate_index_output(const char *); extern void rollback_lock_file(struct lock_file *); extern int delete_ref(const char *, unsigned char *sha1); diff --git a/git-commit.sh b/git-commit.sh index 20c0dc806f..9e0959aec0 100755 --- a/git-commit.sh +++ b/git-commit.sh @@ -370,8 +370,8 @@ t,) # the same way. if test -z "$initial_commit" then - _GIT_INDEX_OUTPUT="$TMP_INDEX" \ - GIT_INDEX_FILE="$THIS_INDEX" git-read-tree -i -m HEAD + GIT_INDEX_FILE="$THIS_INDEX" \ + git-read-tree --index-output="$TMP_INDEX" -i -m HEAD else rm -f "$TMP_INDEX" fi || exit diff --git a/lockfile.c b/lockfile.c index 2023ebb6ff..bed6b21daf 100644 --- a/lockfile.c +++ b/lockfile.c @@ -4,6 +4,7 @@ #include "cache.h" static struct lock_file *lock_file_list; +static const char *alternate_index_output; static void remove_lock_file(void) { @@ -70,11 +71,15 @@ int hold_locked_index(struct lock_file *lk, int die_on_error) return hold_lock_file_for_update(lk, get_index_file(), die_on_error); } +void set_alternate_index_output(const char *name) +{ + alternate_index_output = name; +} + int commit_locked_index(struct lock_file *lk) { - char *output = getenv(INDEX_OUTPUT_ENVIRONMENT); - if (output && *output) { - int result = rename(lk->filename, output); + if (alternate_index_output) { + int result = rename(lk->filename, alternate_index_output); lk->filename[0] = 0; return result; } From 21cd8d00b6908dd8e1d4a5d101c1ba49d3645053 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 30 Mar 2007 01:55:37 -0700 Subject: [PATCH 07/90] add_cache_entry(): removal of file foo does not conflict with foo/bar Similarly, removal of file foo/bar does not conflict with a file foo. Signed-off-by: Junio C Hamano --- read-cache.c | 40 +++++++++++++++++++++++++++++++--------- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/read-cache.c b/read-cache.c index 6339a278da..823c2c3b08 100644 --- a/read-cache.c +++ b/read-cache.c @@ -487,6 +487,8 @@ static int has_file_name(const struct cache_entry *ce, int pos, int ok_to_replac continue; if (p->name[len] != '/') continue; + if (!ce_stage(p) && !p->ce_mode) + continue; retval = -1; if (!ok_to_replace) break; @@ -519,26 +521,37 @@ static int has_dir_name(const struct cache_entry *ce, int pos, int ok_to_replace pos = cache_name_pos(name, ntohs(create_ce_flags(len, stage))); if (pos >= 0) { - retval = -1; - if (!ok_to_replace) - break; - remove_cache_entry_at(pos); - continue; + /* + * Found one, but not so fast. This could + * be a marker that says "I was here, but + * I am being removed". Such an entry is + * not a part of the resulting tree, and + * it is Ok to have a directory at the same + * path. + */ + if (stage || active_cache[pos]->ce_mode) { + retval = -1; + if (!ok_to_replace) + break; + remove_cache_entry_at(pos); + continue; + } } + else + pos = -pos-1; /* * Trivial optimization: if we find an entry that * already matches the sub-directory, then we know * we're ok, and we can exit. */ - pos = -pos-1; while (pos < active_nr) { struct cache_entry *p = active_cache[pos]; if ((ce_namelen(p) <= len) || (p->name[len] != '/') || memcmp(p->name, name, len)) break; /* not our subdirectory */ - if (ce_stage(p) == stage) + if (ce_stage(p) == stage && (stage || p->ce_mode)) /* p is at the same stage as our entry, and * is a subdirectory of what we are looking * at, so we cannot have conflicts at our @@ -562,12 +575,21 @@ static int has_dir_name(const struct cache_entry *ce, int pos, int ok_to_replace */ static int check_file_directory_conflict(const struct cache_entry *ce, int pos, int ok_to_replace) { + int retval; + + /* + * When ce is an "I am going away" entry, we allow it to be added + */ + if (!ce_stage(ce) && !ce->ce_mode) + return 0; + /* * We check if the path is a sub-path of a subsequent pathname * first, since removing those will not change the position - * in the array + * in the array. */ - int retval = has_file_name(ce, pos, ok_to_replace); + retval = has_file_name(ce, pos, ok_to_replace); + /* * Then check if the path might have a clashing sub-directory * before it. From 7f7932ab25f2ddd6080c8b1e8bfd698e523eb7d4 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 2 Apr 2007 00:06:12 -0700 Subject: [PATCH 08/90] unpack_trees.c: pass unpack_trees_options structure to keep_entry() as well. Other decision functions, deleted_entry() and merged_entry() take one as their parameter, and this function should. I'll be introducing a separate index to build the result in, and am planning to pass it as the part of the structure. Signed-off-by: Junio C Hamano --- unpack-trees.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/unpack-trees.c b/unpack-trees.c index ee10eea24c..9c0f4d7651 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -525,7 +525,7 @@ static int deleted_entry(struct cache_entry *ce, struct cache_entry *old, return 1; } -static int keep_entry(struct cache_entry *ce) +static int keep_entry(struct cache_entry *ce, struct unpack_trees_options *o) { add_cache_entry(ce, ADD_CACHE_OK_TO_ADD); return 1; @@ -682,7 +682,7 @@ int threeway_merge(struct cache_entry **stages, if (!head_match || !remote_match) { for (i = 1; i < o->head_idx; i++) { if (stages[i]) { - keep_entry(stages[i]); + keep_entry(stages[i], o); count++; break; } @@ -695,8 +695,8 @@ int threeway_merge(struct cache_entry **stages, show_stage_entry(stderr, "remote ", stages[remote_match]); } #endif - if (head) { count += keep_entry(head); } - if (remote) { count += keep_entry(remote); } + if (head) { count += keep_entry(head, o); } + if (remote) { count += keep_entry(remote, o); } return count; } @@ -728,7 +728,7 @@ int twoway_merge(struct cache_entry **src, (oldtree && newtree && !same(oldtree, newtree) && /* 18 and 19*/ same(current, newtree))) { - return keep_entry(current); + return keep_entry(current, o); } else if (oldtree && !newtree && same(current, oldtree)) { /* 10 or 11 */ @@ -774,7 +774,7 @@ int bind_merge(struct cache_entry **src, if (a && old) die("Entry '%s' overlaps. Cannot bind.", a->name); if (!a) - return keep_entry(old); + return keep_entry(old, o); else return merged_entry(a, NULL, o); } @@ -804,7 +804,7 @@ int oneway_merge(struct cache_entry **src, ce_match_stat(old, &st, 1)) old->ce_flags |= htons(CE_UPDATE); } - return keep_entry(old); + return keep_entry(old, o); } return merged_entry(a, old, o); } From 9a4d8fdc25d6ba08564766c6ded165212f423b61 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 2 Apr 2007 15:06:59 -0700 Subject: [PATCH 09/90] unpack-trees: get rid of *indpos parameter. This variable keeps track of which entry in the original index the traversal is looking at, and belongs to the unpack_trees_options structure along with other traversal status information. Signed-off-by: Junio C Hamano --- unpack-trees.c | 16 +++++++--------- unpack-trees.h | 1 + 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/unpack-trees.c b/unpack-trees.c index 9c0f4d7651..4cf83bd90a 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -70,7 +70,6 @@ static int entcmp(const char *name1, int dir1, const char *name2, int dir2) static int unpack_trees_rec(struct tree_entry_list **posns, int len, const char *base, struct unpack_trees_options *o, - int *indpos, struct tree_entry_list *df_conflict_list) { int baselen = strlen(base); @@ -100,7 +99,7 @@ static int unpack_trees_rec(struct tree_entry_list **posns, int len, cache_name = NULL; /* Check the cache */ - if (o->merge && *indpos < active_nr) { + if (o->merge && o->pos < active_nr) { /* This is a bit tricky: */ /* If the index has a subdirectory (with * contents) as the first name, it'll get a @@ -118,7 +117,7 @@ static int unpack_trees_rec(struct tree_entry_list **posns, int len, * file case. */ - cache_name = active_cache[*indpos]->name; + cache_name = active_cache[o->pos]->name; if (strlen(cache_name) > baselen && !memcmp(cache_name, base, baselen)) { cache_name += baselen; @@ -158,8 +157,8 @@ static int unpack_trees_rec(struct tree_entry_list **posns, int len, if (cache_name && !strcmp(cache_name, first)) { any_files = 1; - src[0] = active_cache[*indpos]; - remove_cache_entry_at(*indpos); + src[0] = active_cache[o->pos]; + remove_cache_entry_at(o->pos); } for (i = 0; i < len; i++) { @@ -228,7 +227,7 @@ static int unpack_trees_rec(struct tree_entry_list **posns, int len, #if DBRT_DEBUG > 1 printf("Added %d entries\n", ret); #endif - *indpos += ret; + o->pos += ret; } else { for (i = 0; i < src_size; i++) { if (src[i]) { @@ -244,7 +243,7 @@ static int unpack_trees_rec(struct tree_entry_list **posns, int len, newbase[baselen + pathlen] = '/'; newbase[baselen + pathlen + 1] = '\0'; if (unpack_trees_rec(subposns, len, newbase, o, - indpos, df_conflict_list)) { + df_conflict_list)) { retval = -1; goto leave_directory; } @@ -375,7 +374,6 @@ static void check_updates(struct cache_entry **src, int nr, int unpack_trees(struct object_list *trees, struct unpack_trees_options *o) { - int indpos = 0; unsigned len = object_list_length(trees); struct tree_entry_list **posns; int i; @@ -404,7 +402,7 @@ int unpack_trees(struct object_list *trees, struct unpack_trees_options *o) posn = posn->next; } if (unpack_trees_rec(posns, len, o->prefix ? o->prefix : "", - o, &indpos, &df_conflict_list)) + o, &df_conflict_list)) return -1; } diff --git a/unpack-trees.h b/unpack-trees.h index 191f7442f1..fee7da4382 100644 --- a/unpack-trees.h +++ b/unpack-trees.h @@ -16,6 +16,7 @@ struct unpack_trees_options { int verbose_update; int aggressive; const char *prefix; + int pos; struct dir_struct *dir; merge_fn_t fn; From 2960a1d9eef846de9cfd9d6e32d203339d792120 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 2 Apr 2007 12:40:19 -0700 Subject: [PATCH 10/90] Fix read-tree --prefix=dir/. The existing code is not wrong per-se, but it started scanning the index from a location that does not match the tree being read, and wasted cycles. Signed-off-by: Junio C Hamano --- builtin-read-tree.c | 1 + 1 file changed, 1 insertion(+) diff --git a/builtin-read-tree.c b/builtin-read-tree.c index 793eae0a5f..5fb84b81a7 100644 --- a/builtin-read-tree.c +++ b/builtin-read-tree.c @@ -228,6 +228,7 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix) if (0 <= pos) die("file '%.*s' already exists.", pfxlen-1, opts.prefix); + opts.pos = -1 - pos; } if (opts.merge) { From b8ba1535bda7bd553f46ea57bdbcee1c90e500a0 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 2 Apr 2007 16:29:56 -0700 Subject: [PATCH 11/90] Fix twoway_merge that passed d/f conflict marker to merged_entry(). When switching from one tree to another, we should not send a marker that says "this file does not exist in the new tree -- I am a placeholder to tell you that, and not a real blob" down to merged_entry() as the result of the merge. --- unpack-trees.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/unpack-trees.c b/unpack-trees.c index 4cf83bd90a..d0367846d4 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -711,12 +711,18 @@ int twoway_merge(struct cache_entry **src, struct unpack_trees_options *o) { struct cache_entry *current = src[0]; - struct cache_entry *oldtree = src[1], *newtree = src[2]; + struct cache_entry *oldtree = src[1]; + struct cache_entry *newtree = src[2]; if (o->merge_size != 2) return error("Cannot do a twoway merge of %d trees", o->merge_size); + if (oldtree == o->df_conflict_entry) + oldtree = NULL; + if (newtree == o->df_conflict_entry) + newtree = NULL; + if (current) { if ((!oldtree && !newtree) || /* 4 and 5 */ (!oldtree && newtree && @@ -724,7 +730,7 @@ int twoway_merge(struct cache_entry **src, (oldtree && newtree && same(oldtree, newtree)) || /* 14 and 15 */ (oldtree && newtree && - !same(oldtree, newtree) && /* 18 and 19*/ + !same(oldtree, newtree) && /* 18 and 19 */ same(current, newtree))) { return keep_entry(current, o); } From c81935348be263de3da085e6ac828b583a85c905 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 15 Mar 2007 23:25:22 -0700 Subject: [PATCH 12/90] Fix switching to a branch with D/F when current branch has file D. This loosens the over-eager verify_absent() check that gets upset to find directory D in the current working tree when switching to a branch that has a file there. The check needs to make sure that we do not lose precious working tree files as a result of removing directory D and replacing it with the file from the other branch, which is a tad expensive but this is a less common case. Signed-off-by: Junio C Hamano --- unpack-trees.c | 113 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 112 insertions(+), 1 deletion(-) diff --git a/unpack-trees.c b/unpack-trees.c index d0367846d4..a0b676903a 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -465,6 +465,64 @@ static void invalidate_ce_path(struct cache_entry *ce) cache_tree_invalidate_path(active_cache_tree, ce->name); } +static int verify_clean_subdirectory(const char *path, const char *action, + struct unpack_trees_options *o) +{ + /* + * we are about to extract "path"; we would not want to lose + * anything in the existing directory there. + */ + int namelen; + int pos, i; + struct dir_struct d; + char *pathbuf; + int cnt = 0; + + /* + * First let's make sure we do not have a local modification + * in that directory. + */ + namelen = strlen(path); + pos = cache_name_pos(path, namelen); + if (0 <= pos) + return cnt; /* we have it as nondirectory */ + pos = -pos - 1; + for (i = pos; i < active_nr; i++) { + struct cache_entry *ce = active_cache[i]; + int len = ce_namelen(ce); + if (len < namelen || + strncmp(path, ce->name, namelen) || + ce->name[namelen] != '/') + break; + /* + * ce->name is an entry in the subdirectory. + */ + if (!ce_stage(ce)) { + verify_uptodate(ce, o); + ce->ce_mode = 0; + } + cnt++; + } + + /* + * Then we need to make sure that we do not lose a locally + * present file that is not ignored. + */ + pathbuf = xmalloc(namelen + 2); + memcpy(pathbuf, path, namelen); + strcpy(pathbuf+namelen, "/"); + + memset(&d, 0, sizeof(d)); + if (o->dir) + d.exclude_per_dir = o->dir->exclude_per_dir; + i = read_directory(&d, path, pathbuf, namelen+1, NULL); + if (i) + die("Updating '%s' would lose untracked files in it", + path); + free(pathbuf); + return cnt; +} + /* * We do not want to remove or overwrite a working tree file that * is not tracked, unless it is ignored. @@ -476,9 +534,62 @@ static void verify_absent(const char *path, const char *action, if (o->index_only || o->reset || !o->update) return; - if (!lstat(path, &st) && !(o->dir && excluded(o->dir, path))) + + if (!lstat(path, &st)) { + int cnt; + + if (o->dir && excluded(o->dir, path)) + /* + * path is explicitly excluded, so it is Ok to + * overwrite it. + */ + return; + if (S_ISDIR(st.st_mode)) { + /* + * We are checking out path "foo" and + * found "foo/." in the working tree. + * This is tricky -- if we have modified + * files that are in "foo/" we would lose + * it. + */ + cnt = verify_clean_subdirectory(path, action, o); + + /* + * If this removed entries from the index, + * what that means is: + * + * (1) the caller unpack_trees_rec() saw path/foo + * in the index, and it has not removed it because + * it thinks it is handling 'path' as blob with + * D/F conflict; + * (2) we will return "ok, we placed a merged entry + * in the index" which would cause o->pos to be + * incremented by one; + * (3) however, original o->pos now has 'path/foo' + * marked with "to be removed". + * + * We need to increment it by the number of + * deleted entries here. + */ + o->pos += cnt; + return; + } + + /* + * The previous round may already have decided to + * delete this path, which is in a subdirectory that + * is being replaced with a blob. + */ + cnt = cache_name_pos(path, strlen(path)); + if (0 <= cnt) { + struct cache_entry *ce = active_cache[cnt]; + if (!ce_stage(ce) && !ce->ce_mode) + return; + } + die("Untracked working tree file '%s' " "would be %s by merge.", path, action); + } } static int merged_entry(struct cache_entry *merge, struct cache_entry *old, From 38a47fd6e316030ba1e72ea447243cb47adf2505 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Wed, 4 Apr 2007 07:12:02 +0200 Subject: [PATCH 13/90] Bisect: teach "bisect start" to optionally use one bad and many good revs. One bad commit is fundamentally needed for bisect to run, and if we beforehand know more good commits, we can narrow the bisect space down without doing the whole tree checkout every time we give good commits. This patch implements: git bisect start [ [...]] [--] [...] as a short-hand for this command sequence: git bisect start git bisect bad $bad git bisect good $good1 $good2... On the other hand, there may be some confusion between revs ( and ...) and ... if -- is not used and if an invalid rev or a pathspec that looks like a rev is given. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- git-bisect.sh | 105 +++++++++++++++++++++++++++++++++--------- t/t6030-bisect-run.sh | 20 ++++++-- 2 files changed, 99 insertions(+), 26 deletions(-) diff --git a/git-bisect.sh b/git-bisect.sh index 11313a7949..2e68e3dac0 100755 --- a/git-bisect.sh +++ b/git-bisect.sh @@ -1,15 +1,24 @@ #!/bin/sh USAGE='[start|bad|good|next|reset|visualize|replay|log|run]' -LONG_USAGE='git bisect start [] reset bisect state and start bisection. -git bisect bad [] mark a known-bad revision. -git bisect good [...] mark ... known-good revisions. -git bisect next find next bisection to test and check it out. -git bisect reset [] finish bisection search and go back to branch. -git bisect visualize show bisect status in gitk. -git bisect replay replay bisection log. -git bisect log show bisect log. -git bisect run ... use ... to automatically bisect.' +LONG_USAGE='git bisect start [ [...]] [--] [...] + reset bisect state and start bisection. +git bisect bad [] + mark a known-bad revision. +git bisect good [...] + mark ... known-good revisions. +git bisect next + find next bisection to test and check it out. +git bisect reset [] + finish bisection search and go back to branch. +git bisect visualize + show bisect status in gitk. +git bisect replay + replay bisection log. +git bisect log + show bisect log. +git bisect run ... + use ... to automatically bisect.' . git-sh-setup require_work_tree @@ -70,14 +79,48 @@ bisect_start() { # # Get rid of any old bisect state # - rm -f "$GIT_DIR/refs/heads/bisect" - rm -rf "$GIT_DIR/refs/bisect/" + bisect_clean_state mkdir "$GIT_DIR/refs/bisect" + + # + # Check for one bad and then some good revisions. + # + has_double_dash=0 + for arg; do + case "$arg" in --) has_double_dash=1; break ;; esac + done + orig_args=$(sq "$@") + bad_seen=0 + while [ $# -gt 0 ]; do + arg="$1" + case "$arg" in + --) + shift + break + ;; + *) + rev=$(git-rev-parse --verify "$arg^{commit}" 2>/dev/null) || { + test $has_double_dash -eq 1 && + die "'$arg' does not appear to be a valid revision" + break + } + if [ $bad_seen -eq 0 ]; then + bad_seen=1 + bisect_write_bad "$rev" + else + bisect_write_good "$rev" + fi + shift + ;; + esac + done + + sq "$@" >"$GIT_DIR/BISECT_NAMES" { printf "git-bisect start" - sq "$@" - } >"$GIT_DIR/BISECT_LOG" - sq "$@" >"$GIT_DIR/BISECT_NAMES" + echo "$orig_args" + } >>"$GIT_DIR/BISECT_LOG" + bisect_auto_next } bisect_bad() { @@ -90,12 +133,17 @@ bisect_bad() { *) usage ;; esac || exit - echo "$rev" >"$GIT_DIR/refs/bisect/bad" - echo "# bad: "$(git-show-branch $rev) >>"$GIT_DIR/BISECT_LOG" + bisect_write_bad "$rev" echo "git-bisect bad $rev" >>"$GIT_DIR/BISECT_LOG" bisect_auto_next } +bisect_write_bad() { + rev="$1" + echo "$rev" >"$GIT_DIR/refs/bisect/bad" + echo "# bad: "$(git-show-branch $rev) >>"$GIT_DIR/BISECT_LOG" +} + bisect_good() { bisect_autostart case "$#" in @@ -106,13 +154,19 @@ bisect_good() { for rev in $revs do rev=$(git-rev-parse --verify "$rev^{commit}") || exit - echo "$rev" >"$GIT_DIR/refs/bisect/good-$rev" - echo "# good: "$(git-show-branch $rev) >>"$GIT_DIR/BISECT_LOG" + bisect_write_good "$rev" echo "git-bisect good $rev" >>"$GIT_DIR/BISECT_LOG" + done bisect_auto_next } +bisect_write_good() { + rev="$1" + echo "$rev" >"$GIT_DIR/refs/bisect/good-$rev" + echo "# good: "$(git-show-branch $rev) >>"$GIT_DIR/BISECT_LOG" +} + bisect_next_check() { next_ok=no test -f "$GIT_DIR/refs/bisect/bad" && @@ -190,14 +244,19 @@ bisect_reset() { usage ;; esac if git checkout "$branch"; then - rm -fr "$GIT_DIR/refs/bisect" - rm -f "$GIT_DIR/refs/heads/bisect" "$GIT_DIR/head-name" - rm -f "$GIT_DIR/BISECT_LOG" - rm -f "$GIT_DIR/BISECT_NAMES" - rm -f "$GIT_DIR/BISECT_RUN" + rm -f "$GIT_DIR/head-name" + bisect_clean_state fi } +bisect_clean_state() { + rm -fr "$GIT_DIR/refs/bisect" + rm -f "$GIT_DIR/refs/heads/bisect" + rm -f "$GIT_DIR/BISECT_LOG" + rm -f "$GIT_DIR/BISECT_NAMES" + rm -f "$GIT_DIR/BISECT_RUN" +} + bisect_replay () { test -r "$1" || { echo >&2 "cannot read $1 for replaying" diff --git a/t/t6030-bisect-run.sh b/t/t6030-bisect-run.sh index 39c72283b5..455dc60812 100755 --- a/t/t6030-bisect-run.sh +++ b/t/t6030-bisect-run.sh @@ -40,8 +40,8 @@ test_expect_success \ # We want to automatically find the commit that # introduced "Another" into hello. test_expect_success \ - 'git bisect run simple case' \ - 'echo "#!/bin/sh" > test_script.sh && + '"git bisect run" simple case' \ + 'echo "#"\!"/bin/sh" > test_script.sh && echo "grep Another hello > /dev/null" >> test_script.sh && echo "test \$? -ne 0" >> test_script.sh && chmod +x test_script.sh && @@ -49,7 +49,21 @@ test_expect_success \ git bisect good $HASH1 && git bisect bad $HASH4 && git bisect run ./test_script.sh > my_bisect_log.txt && - grep "$HASH3 is first bad commit" my_bisect_log.txt' + grep "$HASH3 is first bad commit" my_bisect_log.txt && + git bisect reset' + +# We want to automatically find the commit that +# introduced "Ciao" into hello. +test_expect_success \ + '"git bisect run" with more complex "git bisect start"' \ + 'echo "#"\!"/bin/sh" > test_script.sh && + echo "grep Ciao hello > /dev/null" >> test_script.sh && + echo "test \$? -ne 0" >> test_script.sh && + chmod +x test_script.sh && + git bisect start $HASH4 $HASH1 && + git bisect run ./test_script.sh > my_bisect_log.txt && + grep "$HASH4 is first bad commit" my_bisect_log.txt && + git bisect reset' # # From 6fe9c570ccd9f893d9a981cd8ea5f51dc21ceec8 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Thu, 5 Apr 2007 05:33:53 +0200 Subject: [PATCH 14/90] Documentation: bisect: "start" accepts one bad and many good commits Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- Documentation/git-bisect.txt | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/Documentation/git-bisect.txt b/Documentation/git-bisect.txt index b2bc58d851..5f68ee1584 100644 --- a/Documentation/git-bisect.txt +++ b/Documentation/git-bisect.txt @@ -15,7 +15,7 @@ DESCRIPTION The command takes various subcommands, and different options depending on the subcommand: - git bisect start [...] + git bisect start [ [...]] [--] [...] git bisect bad git bisect good git bisect reset [] @@ -134,15 +134,26 @@ $ git reset --hard HEAD~3 # try 3 revs before what Then compile and test the one you chose to try. After that, tell bisect what the result was as usual. -Cutting down bisection by giving path parameter to bisect start -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Cutting down bisection by giving more parameters to bisect start +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ You can further cut down the number of trials if you know what part of the tree is involved in the problem you are tracking down, by giving paths parameters when you say `bisect start`, like this: ------------ -$ git bisect start arch/i386 include/asm-i386 +$ git bisect start -- arch/i386 include/asm-i386 +------------ + +If you know beforehand more than one good commits, you can narrow the +bisect space down without doing the whole tree checkout every time you +give good commits. You give the bad revision immediately after `start` +and then you give all the good revisions you have: + +------------ +$ git bisect start v2.6.20-rc6 v2.6.20-rc4 v2.6.20-rc1 -- + # v2.6.20-rc6 is bad + # v2.6.20-rc4 and v2.6.20-rc1 are good ------------ Bisect run From 3b486cd229f1db437462a45103ef01d50c47a00d Mon Sep 17 00:00:00 2001 From: "Fernando J. Pereda" Date: Wed, 4 Apr 2007 22:42:33 +0200 Subject: [PATCH 15/90] Makefile: Add '+' to QUIET_SUBDIR0 to fix parallel make. Signed-off-by: Fernando J. Pereda Signed-off-by: Junio C Hamano --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 507ad9103e..c9a84aa25a 100644 --- a/Makefile +++ b/Makefile @@ -641,7 +641,7 @@ ifeq ($(TCLTK_PATH),) NO_TCLTK=NoThanks endif -QUIET_SUBDIR0 = $(MAKE) -C # space to separate -C and subdir +QUIET_SUBDIR0 = +$(MAKE) -C # space to separate -C and subdir QUIET_SUBDIR1 = ifneq ($(findstring $(MAKEFLAGS),w),w) @@ -657,7 +657,7 @@ ifndef V QUIET_LINK = @echo ' ' LINK $@; QUIET_BUILT_IN = @echo ' ' BUILTIN $@; QUIET_GEN = @echo ' ' GEN $@; - QUIET_SUBDIR0 = @subdir= + QUIET_SUBDIR0 = +@subdir= QUIET_SUBDIR1 = ;$(NO_SUBDIR) echo ' ' SUBDIR $$subdir; \ $(MAKE) $(PRINT_DIR) -C $$subdir export V From 6fecf1915c5fd0b14e2ca2ec9e1a6b620abfb5c2 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 5 Apr 2007 22:51:14 -0700 Subject: [PATCH 16/90] git-bisect: modernization This slightly modernizes the bisect script to use show-ref/for-each-ref instead of looking into $GIT_DIR/refs files directly. Signed-off-by: Junio C Hamano --- git-bisect.sh | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/git-bisect.sh b/git-bisect.sh index 2e68e3dac0..c936533883 100755 --- a/git-bisect.sh +++ b/git-bisect.sh @@ -169,11 +169,10 @@ bisect_write_good() { bisect_next_check() { next_ok=no - test -f "$GIT_DIR/refs/bisect/bad" && - case "$(cd "$GIT_DIR" && echo refs/bisect/good-*)" in - refs/bisect/good-\*) ;; - *) next_ok=yes ;; - esac + git show-ref -q --verify refs/bisect/bad && + test -n "$(git for-each-ref "refs/bisect/good-*")" && + next_ok=yes + case "$next_ok,$1" in no,) false ;; no,fail) From edbe4466746e577418b48922003a2c8a9cb55bee Mon Sep 17 00:00:00 2001 From: Frank Lichtenheld Date: Fri, 6 Apr 2007 23:52:39 +0200 Subject: [PATCH 17/90] cvsimport: sync usage lines with existing options Sync both the usage lines in the code and the asciidoc documentation with the real list of options. While all options seems to be documented in the asciidoc document, not all of them were listed in the usage line. Signed-off-by: Frank Lichtenheld Signed-off-by: Junio C Hamano --- Documentation/git-cvsimport.txt | 8 +++++--- git-cvsimport.perl | 5 +++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/Documentation/git-cvsimport.txt b/Documentation/git-cvsimport.txt index 0d59c06139..8374d90471 100644 --- a/Documentation/git-cvsimport.txt +++ b/Documentation/git-cvsimport.txt @@ -9,9 +9,11 @@ git-cvsimport - Salvage your data out of another SCM people love to hate SYNOPSIS -------- [verse] -'git-cvsimport' [-o ] [-h] [-v] [-d ] [-s ] - [-p ] [-C ] [-i] [-P ] - [-m] [-M regex] [] +'git-cvsimport' [-o ] [-h] [-v] [-d ] + [-A ] [-p ] [-P ] + [-C ] [-z ] [-i] [-k] [-u] [-s ] + [-a] [-m] [-M ] [-S ] [-L ] + [] DESCRIPTION diff --git a/git-cvsimport.perl b/git-cvsimport.perl index 1a1ba7b1a6..d3fbcbc15c 100755 --- a/git-cvsimport.perl +++ b/git-cvsimport.perl @@ -36,8 +36,9 @@ sub usage() { print STDERR < Date: Fri, 6 Apr 2007 23:52:40 +0200 Subject: [PATCH 18/90] cvsimport: Improve documentation of CVSROOT and CVS module determination Document the fact that git-cvsimport tries to find out CVSROOT from CVS/Root and $ENV{CVSROOT} and CVS_module from CVS/Repository. Also use ` ` syntax for all filenames for consistency. Signed-off-by: Frank Lichtenheld Signed-off-by: Junio C Hamano --- Documentation/git-cvsimport.txt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Documentation/git-cvsimport.txt b/Documentation/git-cvsimport.txt index 8374d90471..6a0821a37c 100644 --- a/Documentation/git-cvsimport.txt +++ b/Documentation/git-cvsimport.txt @@ -35,7 +35,9 @@ OPTIONS -d :: The root of the CVS archive. May be local (a simple path) or remote; currently, only the :local:, :ext: and :pserver: access methods - are supported. + are supported. If not given, git-cvsimport will try to read it + from `CVS/Root`. If no such file exists, it checks for the + `CVSROOT` environment variable. -C :: The git repository to import to. If the directory doesn't @@ -87,6 +89,8 @@ If you need to pass multiple options, separate them with a comma. :: The CVS module you want to import. Relative to . + If not given, git-cvsimport tries to read it from + `CVS/Repository`. -h:: Print a short usage message and exit. @@ -124,7 +128,7 @@ git-cvsimport will make it appear as those authors had their GIT_AUTHOR_NAME and GIT_AUTHOR_EMAIL set properly all along. + -For convenience, this data is saved to $GIT_DIR/cvs-authors +For convenience, this data is saved to `$GIT_DIR/cvs-authors` each time the -A option is provided and read from that same file each time git-cvsimport is run. + From 7bf77644e70d881f407c77e6d65ebe70c90916d6 Mon Sep 17 00:00:00 2001 From: Frank Lichtenheld Date: Fri, 6 Apr 2007 23:52:41 +0200 Subject: [PATCH 19/90] cvsimport: Improve usage error reporting Actually tell the user what he did wrong in case of usage errors instead of only printing the general usage information. Signed-off-by: Frank Lichtenheld Signed-off-by: Junio C Hamano --- git-cvsimport.perl | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/git-cvsimport.perl b/git-cvsimport.perl index d3fbcbc15c..ac74bc51b3 100755 --- a/git-cvsimport.perl +++ b/git-cvsimport.perl @@ -32,7 +32,9 @@ $ENV{'TZ'}="UTC"; our ($opt_h,$opt_o,$opt_v,$opt_k,$opt_u,$opt_d,$opt_p,$opt_C,$opt_z,$opt_i,$opt_P, $opt_s,$opt_m,$opt_M,$opt_A,$opt_S,$opt_L, $opt_a); my (%conv_author_name, %conv_author_email); -sub usage() { +sub usage(;$) { + my $msg = shift; + print(STDERR "Error: $msg\n") if $msg; print STDERR < Date: Fri, 6 Apr 2007 23:52:42 +0200 Subject: [PATCH 20/90] cvsimport: Reorder options in documentation for better understanding The current order the options are documented in makes no sense at all to me. Reorder them so that similar options are grouped together and also order them somehwhat by importance. Signed-off-by: Frank Lichtenheld Signed-off-by: Junio C Hamano --- Documentation/git-cvsimport.txt | 54 ++++++++++++++++----------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/Documentation/git-cvsimport.txt b/Documentation/git-cvsimport.txt index 6a0821a37c..a5276f776f 100644 --- a/Documentation/git-cvsimport.txt +++ b/Documentation/git-cvsimport.txt @@ -32,6 +32,9 @@ any CVS branches, yourself. OPTIONS ------- +-v:: + Verbosity: let 'cvsimport' report what it is doing. + -d :: The root of the CVS archive. May be local (a simple path) or remote; currently, only the :local:, :ext: and :pserver: access methods @@ -39,10 +42,23 @@ OPTIONS from `CVS/Root`. If no such file exists, it checks for the `CVSROOT` environment variable. +:: + The CVS module you want to import. Relative to . + If not given, git-cvsimport tries to read it from + `CVS/Repository`. + -C :: The git repository to import to. If the directory doesn't exist, it will be created. Default is the current directory. +-o :: + The 'HEAD' branch from CVS is imported to the 'origin' branch within + the git repository, as 'HEAD' already has a special meaning for git. + Use this option if you want to import into a different branch. ++ +Use '-o master' for continuing an import that was initially done by +the old cvs2git tool. + -i:: Import-only: don't perform a checkout after importing. This option ensures the working directory and index remain untouched and will @@ -56,13 +72,8 @@ OPTIONS -u:: Convert underscores in tag and branch names to dots. --o :: - The 'HEAD' branch from CVS is imported to the 'origin' branch within - the git repository, as 'HEAD' already has a special meaning for git. - Use this option if you want to import into a different branch. -+ -Use '-o master' for continuing an import that was initially done by -the old cvs2git tool. +-s :: + Substitute the character "/" in branch names with -p :: Additional options for cvsps. @@ -70,6 +81,10 @@ the old cvs2git tool. + If you need to pass multiple options, separate them with a comma. +-z :: + Pass the timestamp fuzz factor to cvsps, in seconds. If unset, + cvsps defaults to 300s. + -P :: Instead of calling cvsps, read the provided cvsps output file. Useful for debugging or when cvsps is being handled outside cvsimport. @@ -84,31 +99,13 @@ If you need to pass multiple options, separate them with a comma. regex. It can be used with -m to also see the default regexes. You must escape forward slashes. --v:: - Verbosity: let 'cvsimport' report what it is doing. - -:: - The CVS module you want to import. Relative to . - If not given, git-cvsimport tries to read it from - `CVS/Repository`. - --h:: - Print a short usage message and exit. - --z :: - Pass the timestamp fuzz factor to cvsps, in seconds. If unset, - cvsps defaults to 300s. - --s :: - Substitute the character "/" in branch names with +-S :: + Skip paths matching the regex. -a:: Import all commits, including recent ones. cvsimport by default skips commits that have a timestamp less than 10 minutes ago. --S :: - Skip paths matching the regex. - -L :: Limit the number of commits imported. Workaround for cases where cvsimport leaks memory. @@ -136,6 +133,9 @@ It is not recommended to use this feature if you intend to export changes back to CVS again later with gitlink:git-cvsexportcommit[1]. +-h:: + Print a short usage message and exit. + OUTPUT ------ If '-v' is specified, the script reports what it is doing. From 0e070f997b529e81f7daff79fea1b61bc9166f6b Mon Sep 17 00:00:00 2001 From: Frank Lichtenheld Date: Fri, 6 Apr 2007 23:52:43 +0200 Subject: [PATCH 21/90] cvsimport: Improve formating consistency Use ' ' syntax for all commandline options mentioned in text. Signed-off-by: Frank Lichtenheld Signed-off-by: Junio C Hamano --- Documentation/git-cvsimport.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Documentation/git-cvsimport.txt b/Documentation/git-cvsimport.txt index a5276f776f..e0be856546 100644 --- a/Documentation/git-cvsimport.txt +++ b/Documentation/git-cvsimport.txt @@ -65,7 +65,7 @@ the old cvs2git tool. not create them if they do not exist. -k:: - Kill keywords: will extract files with -kk from the CVS archive + Kill keywords: will extract files with '-kk' from the CVS archive to avoid noisy changesets. Highly recommended, but off by default to preserve compatibility with early imported trees. @@ -96,7 +96,7 @@ If you need to pass multiple options, separate them with a comma. -M :: Attempt to detect merges based on the commit message with a custom - regex. It can be used with -m to also see the default regexes. + regex. It can be used with '-m' to also see the default regexes. You must escape forward slashes. -S :: @@ -126,7 +126,7 @@ their GIT_AUTHOR_NAME and GIT_AUTHOR_EMAIL set properly all along. + For convenience, this data is saved to `$GIT_DIR/cvs-authors` -each time the -A option is provided and read from that same +each time the '-A' option is provided and read from that same file each time git-cvsimport is run. + It is not recommended to use this feature if you intend to From a925b89cea15c90ccf131a4ec9770ca1323e4a7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?YOSHIFUJI=20Hideaki=20/=20=E5=90=89=E8=97=A4=E8=8B=B1?= =?UTF-8?q?=E6=98=8E?= Date: Fri, 6 Apr 2007 08:50:24 +0900 Subject: [PATCH 22/90] Avoid composing too long "References" header. The number of characters in a line MUST be no more than 998 characters, and SHOULD be no more than 78 characters (RFC2822). It is much safer to fold the header by ourselves. Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: Junio C Hamano --- git-send-email.perl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-send-email.perl b/git-send-email.perl index ae50990d08..1278fcba46 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -595,7 +595,7 @@ foreach my $t (@files) { if ($chain_reply_to || !defined $reply_to || length($reply_to) == 0) { $reply_to = $message_id; if (length $references > 0) { - $references .= " $message_id"; + $references .= "\n $message_id"; } else { $references = "$message_id"; } From 08b984fb5456c23b04c573f9021141ce27af707d Mon Sep 17 00:00:00 2001 From: Brian Gernhardt Date: Fri, 6 Apr 2007 01:42:04 -0400 Subject: [PATCH 23/90] Distinguish branches by more than case in tests. The renaming without config test changed a branch from q to Q, which fails on non-case sensitive file systems. Change the test to use q and q2. Signed-off-by: Junio C Hamano --- t/t3200-branch.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh index ce2c5f41fd..3ca1a325d4 100755 --- a/t/t3200-branch.sh +++ b/t/t3200-branch.sh @@ -85,9 +85,9 @@ test_expect_failure \ mv .git/config .git/config-saved -test_expect_success 'git branch -m q Q without config should succeed' ' - git-branch -m q Q && - git-branch -m Q q +test_expect_success 'git branch -m q q2 without config should succeed' ' + git-branch -m q q2 && + git-branch -m q2 q ' mv .git/config-saved .git/config From d26f9fef470443dfb0d09c08341634208f4fb6f7 Mon Sep 17 00:00:00 2001 From: Lars Hjemli Date: Fri, 6 Apr 2007 10:33:06 +0200 Subject: [PATCH 24/90] rename_ref(): only print a warning when config-file update fails If git_config_rename_section() fails, rename_ref() used to return 1, which left HEAD pointing to an absent refs/heads file (since the actual renaming had already occurred). Signed-off-by: Lars Hjemli Signed-off-by: Junio C Hamano --- refs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/refs.c b/refs.c index f471152bfc..2ac6384949 100644 --- a/refs.c +++ b/refs.c @@ -835,7 +835,7 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg) snprintf(oldsection, 1024, "branch.%s", oldref + 11); snprintf(newsection, 1024, "branch.%s", newref + 11); if (git_config_rename_section(oldsection, newsection) < 0) - return 1; + error("unable to update config-file"); } return 0; From 19eba1515a7d3b0dac6d4ee73492d978c3c2149b Mon Sep 17 00:00:00 2001 From: Lars Hjemli Date: Fri, 6 Apr 2007 14:13:00 +0200 Subject: [PATCH 25/90] Make builtin-branch.c handle the git config file This moves the knowledge about .git/config usage out of refs.c and into builtin-branch.c instead, which allows git-branch to update HEAD to point at the moved branch before attempting to update the config file. It also allows git-branch to exit with an error code if updating the config file should fail. Signed-off-by: Lars Hjemli Signed-off-by: Junio C Hamano --- builtin-branch.c | 6 ++++++ refs.c | 10 ---------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/builtin-branch.c b/builtin-branch.c index a4494ee337..7408285050 100644 --- a/builtin-branch.c +++ b/builtin-branch.c @@ -493,6 +493,7 @@ static void rename_branch(const char *oldname, const char *newname, int force) { char oldref[PATH_MAX], newref[PATH_MAX], logmsg[PATH_MAX*2 + 100]; unsigned char sha1[20]; + char oldsection[PATH_MAX], newsection[PATH_MAX]; if (!oldname) die("cannot rename the current branch while not on any."); @@ -521,6 +522,11 @@ static void rename_branch(const char *oldname, const char *newname, int force) /* no need to pass logmsg here as HEAD didn't really move */ if (!strcmp(oldname, head) && create_symref("HEAD", newref, NULL)) die("Branch renamed to %s, but HEAD is not updated!", newname); + + snprintf(oldsection, sizeof(oldsection), "branch.%s", oldref + 11); + snprintf(newsection, sizeof(newsection), "branch.%s", newref + 11); + if (git_config_rename_section(oldsection, newsection) < 0) + die("Branch is renamed, but update of config-file failed"); } int cmd_branch(int argc, const char **argv, const char *prefix) diff --git a/refs.c b/refs.c index 2ac6384949..d2b7b7fb56 100644 --- a/refs.c +++ b/refs.c @@ -828,16 +828,6 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg) goto rollback; } - if (!prefixcmp(oldref, "refs/heads/") && - !prefixcmp(newref, "refs/heads/")) { - char oldsection[1024], newsection[1024]; - - snprintf(oldsection, 1024, "branch.%s", oldref + 11); - snprintf(newsection, 1024, "branch.%s", newref + 11); - if (git_config_rename_section(oldsection, newsection) < 0) - error("unable to update config-file"); - } - return 0; rollback: From ae25c67acae4971594b574e4fc02b131a3da156a Mon Sep 17 00:00:00 2001 From: Arjen Laarhoven Date: Sat, 7 Apr 2007 01:48:36 +0200 Subject: [PATCH 26/90] usermanual.txt: some capitalization nits Signed-off-by: Arjen Laarhoven Signed-off-by: Junio C Hamano --- Documentation/user-manual.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt index 574e9c0e50..d43d2377ec 100644 --- a/Documentation/user-manual.txt +++ b/Documentation/user-manual.txt @@ -1015,7 +1015,7 @@ $ git commit ------------------------------------------------- [[how-to-make-a-commit]] -how to make a commit +How to make a commit -------------------- Creating a new commit takes three steps: @@ -1109,7 +1109,7 @@ $ git diff # difference between the index file and your $ git status # a brief per-file summary of the above. ------------------------------------------------- -creating good commit messages +Creating good commit messages ----------------------------- Though not required, it's a good idea to begin the commit message @@ -1119,7 +1119,7 @@ description. Tools that turn commits into email, for example, use the first line on the Subject line and the rest of the commit in the body. -how to merge +How to merge ------------ You can rejoin two diverging branches of development using @@ -1298,7 +1298,7 @@ the different stages of that file will be "collapsed", after which git-diff will (by default) no longer show diffs for that file. [[undoing-a-merge]] -undoing a merge +Undoing a merge --------------- If you get stuck and decide to just give up and throw the whole mess From 4c84f0dcc630817aaf71049163ee63b96d543a38 Mon Sep 17 00:00:00 2001 From: Arjen Laarhoven Date: Sat, 7 Apr 2007 01:48:54 +0200 Subject: [PATCH 27/90] t3200-branch.sh: small language nit Signed-off-by: Junio C Hamano --- t/t3200-branch.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh index 3ca1a325d4..828d553a4b 100755 --- a/t/t3200-branch.sh +++ b/t/t3200-branch.sh @@ -11,7 +11,7 @@ handled. Specifically, that a bogus branch is not created. . ./test-lib.sh test_expect_success \ - 'prepare an trivial repository' \ + 'prepare a trivial repository' \ 'echo Hello > A && git-update-index --add A && git-commit -m "Initial commit." && From 4f50671699090089b7967880f9b0291391c8de1a Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 5 Apr 2007 22:52:37 -0700 Subject: [PATCH 28/90] t6030: add a bit more tests to git-bisect Verify that git-bisect does not start before getting one bad and one good commit. Signed-off-by: Junio C Hamano --- t/t6030-bisect-run.sh | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/t/t6030-bisect-run.sh b/t/t6030-bisect-run.sh index 455dc60812..4910ff6841 100755 --- a/t/t6030-bisect-run.sh +++ b/t/t6030-bisect-run.sh @@ -2,7 +2,7 @@ # # Copyright (c) 2007 Christian Couder # -test_description='Tests git-bisect run functionality' +test_description='Tests git-bisect functionality' . ./test-lib.sh @@ -37,6 +37,42 @@ test_expect_success \ HASH3=$(git rev-list HEAD | head -2 | tail -1) && HASH4=$(git rev-list HEAD | head -1)' +test_expect_success 'bisect does not start with only one bad' ' + git bisect reset && + git bisect start && + git bisect bad $HASH4 || return 1 + + if git bisect next + then + echo Oops, should have failed. + false + else + : + fi +' + +test_expect_success 'bisect does not start with only one good' ' + git bisect reset && + git bisect start && + git bisect good $HASH1 || return 1 + + if git bisect next + then + echo Oops, should have failed. + false + else + : + fi +' + +test_expect_success 'bisect start with one bad and good' ' + git bisect reset && + git bisect start && + git bisect good $HASH1 && + git bisect bad $HASH4 && + git bisect next +' + # We want to automatically find the commit that # introduced "Another" into hello. test_expect_success \ From 0a5280a9f444c33b0e4ebf2f073df5899c112cf8 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 5 Apr 2007 23:27:44 -0700 Subject: [PATCH 29/90] git-bisect: allow bisecting with only one bad commit. This allows you to say: git bisect start git bisect bad $bad git bisect next to start bisection without knowing a good commit. This would have you try a commit that is half-way since the beginning of the history, which is rather wasteful if you already know a good commit, but if you don't (or your history is short enough that you do not care), there is no reason not to allow this. Signed-off-by: Junio C Hamano --- git-bisect.sh | 85 ++++++++++++++++++++++++++----------------- t/t6030-bisect-run.sh | 17 +++------ 2 files changed, 58 insertions(+), 44 deletions(-) diff --git a/git-bisect.sh b/git-bisect.sh index c936533883..85c374e21e 100755 --- a/git-bisect.sh +++ b/git-bisect.sh @@ -168,26 +168,40 @@ bisect_write_good() { } bisect_next_check() { - next_ok=no - git show-ref -q --verify refs/bisect/bad && - test -n "$(git for-each-ref "refs/bisect/good-*")" && - next_ok=yes + missing_good= missing_bad= + git show-ref -q --verify refs/bisect/bad || missing_bad=t + test -n "$(git for-each-ref "refs/bisect/good-*")" || missing_good=t - case "$next_ok,$1" in - no,) false ;; - no,fail) - THEN='' - test -d "$GIT_DIR/refs/bisect" || { - echo >&2 'You need to start by "git bisect start".' - THEN='then ' - } - echo >&2 'You '$THEN'need to give me at least one good' \ - 'and one bad revisions.' - echo >&2 '(You can use "git bisect bad" and' \ - '"git bisect good" for that.)' - exit 1 ;; + case "$missing_good,$missing_bad,$1" in + ,,*) + : have both good and bad - ok + ;; + *,) + # do not have both but not asked to fail - just report. + false + ;; + t,,good) + # have bad but not good. we could bisect although + # this is less optimum. + echo >&2 'Warning: bisecting only with a bad commit.' + if test -t 0 + then + printf >&2 'Are you sure [Y/n]? ' + case "$(read yesno)" in [Nn]*) exit 1 ;; esac + fi + : bisect without good... + ;; *) - true ;; + THEN='' + test -d "$GIT_DIR/refs/bisect" || { + echo >&2 'You need to start by "git bisect start".' + THEN='then ' + } + echo >&2 'You '$THEN'need to give me at least one good' \ + 'and one bad revisions.' + echo >&2 '(You can use "git bisect bad" and' \ + '"git bisect good" for that.)' + exit 1 ;; esac } @@ -198,27 +212,32 @@ bisect_auto_next() { bisect_next() { case "$#" in 0) ;; *) usage ;; esac bisect_autostart - bisect_next_check fail + bisect_next_check good + bad=$(git-rev-parse --verify refs/bisect/bad) && - good=$(git-rev-parse --sq --revs-only --not \ - $(cd "$GIT_DIR" && ls refs/bisect/good-*)) && - rev=$(eval "git-rev-list --bisect $good $bad -- $(cat "$GIT_DIR/BISECT_NAMES")") || exit - if [ -z "$rev" ]; then - echo "$bad was both good and bad" - exit 1 + good=$(git for-each-ref --format='^%(objectname)' \ + "refs/bisect/good-*" | tr '[\012]' ' ') && + eval="git-rev-list --bisect-vars $good $bad --" && + eval="$eval $(cat "$GIT_DIR/BISECT_NAMES")" && + eval=$(eval "$eval") && + eval "$eval" || exit + + if [ -z "$bisect_rev" ]; then + echo "$bad was both good and bad" + exit 1 fi - if [ "$rev" = "$bad" ]; then - echo "$rev is first bad commit" - git-diff-tree --pretty $rev - exit 0 + if [ "$bisect_rev" = "$bad" ]; then + echo "$bisect_rev is first bad commit" + git-diff-tree --pretty $bisect_rev + exit 0 fi - nr=$(eval "git-rev-list $rev $good -- $(cat $GIT_DIR/BISECT_NAMES)" | wc -l) || exit - echo "Bisecting: $nr revisions left to test after this" - echo "$rev" > "$GIT_DIR/refs/heads/new-bisect" + + echo "Bisecting: $bisect_nr revisions left to test after this" + echo "$bisect_rev" >"$GIT_DIR/refs/heads/new-bisect" git checkout -q new-bisect || exit mv "$GIT_DIR/refs/heads/new-bisect" "$GIT_DIR/refs/heads/bisect" && GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD refs/heads/bisect - git-show-branch "$rev" + git-show-branch "$bisect_rev" } bisect_visualize() { diff --git a/t/t6030-bisect-run.sh b/t/t6030-bisect-run.sh index 4910ff6841..de3123522a 100755 --- a/t/t6030-bisect-run.sh +++ b/t/t6030-bisect-run.sh @@ -4,6 +4,8 @@ # test_description='Tests git-bisect functionality' +exec Date: Sat, 7 Apr 2007 01:49:03 +0200 Subject: [PATCH 30/90] t5300-pack-object.sh: portability issue using /usr/bin/stat In the test 'compare delta flavors', /usr/bin/stat is used to get file size. This isn't portable. There already is a dependency on Perl, use its '-s' operator to get the file size. Signed-off-by: Arjen Laarhoven Signed-off-by: Junio C Hamano --- t/t5300-pack-object.sh | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/t/t5300-pack-object.sh b/t/t5300-pack-object.sh index 35e036a864..083095f7f3 100755 --- a/t/t5300-pack-object.sh +++ b/t/t5300-pack-object.sh @@ -123,11 +123,12 @@ test_expect_success \ done' cd "$TRASH" -test_expect_success \ - 'compare delta flavors' \ - 'size_2=`stat -c "%s" test-2-${packname_2}.pack` && - size_3=`stat -c "%s" test-3-${packname_3}.pack` && - test $size_2 -gt $size_3' +test_expect_success 'compare delta flavors' ' + perl -e '\'' + defined($_ = -s $_) or die for @ARGV; + exit 1 if $ARGV[0] <= $ARGV[1]; + '\'' test-2-$packname_2.pack test-3-$packname_3.pack +' rm -fr .git2 mkdir .git2 From 63b4b7a7ed657f406eb274f88a7e2c61fd6fe958 Mon Sep 17 00:00:00 2001 From: Arjen Laarhoven Date: Sat, 7 Apr 2007 01:49:17 +0200 Subject: [PATCH 31/90] Makefile: iconv() on Darwin has the old interface The libiconv on Darwin uses the old iconv() interface (2nd argument is a const char **, instead of a char **). Add OLD_ICONV to the Darwin variable definitions to handle this. Signed-off-by: Arjen Laarhoven Acked-by: Brian Gernhardt Signed-off-by: Junio C Hamano --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index 81dafe5ee3..dcdaa02e47 100644 --- a/Makefile +++ b/Makefile @@ -356,6 +356,7 @@ endif ifeq ($(uname_S),Darwin) NEEDS_SSL_WITH_CRYPTO = YesPlease NEEDS_LIBICONV = YesPlease + OLD_ICONV = UnfortunatelyYes NO_STRLCPY = YesPlease endif ifeq ($(uname_S),SunOS) From d79073922fcb8c8a0bd57112817a2154f1ed05c1 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 5 Apr 2007 22:17:20 -0700 Subject: [PATCH 32/90] Documentation: tighten dependency for git.{html,txt} Every time _any_ documentation page changed, cmds-*.txt files were regenerated, which caused git.{html,txt} to be remade. Try not to update cmds-*.txt files if their new contents match the old ones. Signed-off-by: Junio C Hamano --- Documentation/Makefile | 7 +++++-- Documentation/cmd-list.perl | 14 ++++++++++++-- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/Documentation/Makefile b/Documentation/Makefile index 7db3fb992f..ad87736b0c 100644 --- a/Documentation/Makefile +++ b/Documentation/Makefile @@ -85,14 +85,17 @@ cmds_txt = cmds-ancillaryinterrogators.txt \ cmds-purehelpers.txt \ cmds-foreignscminterface.txt -$(cmds_txt): cmd-list.perl $(MAN1_TXT) +$(cmds_txt): cmd-list.made + +cmd-list.made: cmd-list.perl $(MAN1_TXT) perl ./cmd-list.perl + date >$@ git.7 git.html: git.txt core-intro.txt clean: rm -f *.xml *.html *.1 *.7 howto-index.txt howto/*.html doc.dep - rm -f $(cmds_txt) + rm -f $(cmds_txt) *.made %.html : %.txt $(ASCIIDOC) -b xhtml11 -d manpage -f asciidoc.conf $(ASCIIDOC_EXTRA) $< diff --git a/Documentation/cmd-list.perl b/Documentation/cmd-list.perl index b54382b2bf..0381590d38 100755 --- a/Documentation/cmd-list.perl +++ b/Documentation/cmd-list.perl @@ -1,8 +1,11 @@ -# +#!/usr/bin/perl -w + +use File::Compare qw(compare); sub format_one { my ($out, $name) = @_; my ($state, $description); + $state = 0; open I, '<', "$name.txt" or die "No such file $name.txt"; while () { if (/^NAME$/) { @@ -55,7 +58,14 @@ for my $cat (qw(ancillaryinterrogators format_one(\*O, $_); } close O; - rename "$out+", "$out"; + + if (-f "$out" && compare("$out", "$out+") == 0) { + unlink "$out+"; + } + else { + print STDERR "$out\n"; + rename "$out+", "$out"; + } } __DATA__ From 39878b0cb7d0afc3ff4c8adb715815f116188178 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 6 Apr 2007 23:04:55 -0700 Subject: [PATCH 33/90] git-push reports the URL after failing. This came up on #git when somebody was getting 'unable to create ./objects/tmp_oXXXX' but sweared he had write permission to that directory. It turned out that the repository URL was changed and he was accessing a repository he does not have a write permission anymore. I am not sure how much this would have helped somebody who believed he was accessing location when the permission of that location was changed while he was looking the other way, though. But giving more information on the error path would be better, and the next change would be helped with this as well. Signed-off-by: Junio C Hamano --- builtin-push.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/builtin-push.c b/builtin-push.c index 70b1168fa6..23143be542 100644 --- a/builtin-push.c +++ b/builtin-push.c @@ -339,6 +339,8 @@ static int do_push(const char *repo) err = run_command_v_opt(argv, RUN_GIT_CMD); if (!err) continue; + + error("failed to push to '%s'", uri[i]); switch (err) { case -ERR_RUN_COMMAND_FORK: die("unable to fork for %s", sender); From fd1d1b05e937ff3164be38b111e5d76d592ee548 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 6 Apr 2007 23:04:53 -0700 Subject: [PATCH 34/90] git-push to multiple locations does not stop at the first failure When pushing into multiple repositories with git push, via multiple URL in .git/remotes/$shorthand or multiple url variables in [remote "$shorthand"] section, we used to stop upon the first failure. Continue the operation and report the failure at the end. Signed-off-by: Junio C Hamano --- builtin-push.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/builtin-push.c b/builtin-push.c index 23143be542..cb78401c94 100644 --- a/builtin-push.c +++ b/builtin-push.c @@ -297,7 +297,7 @@ static int read_config(const char *repo, const char *uri[MAX_URI]) static int do_push(const char *repo) { const char *uri[MAX_URI]; - int i, n; + int i, n, errs; int common_argc; const char **argv; int argc; @@ -317,6 +317,7 @@ static int do_push(const char *repo) argv[argc++] = receivepack; common_argc = argc; + errs = 0; for (i = 0; i < n; i++) { int err; int dest_argc = common_argc; @@ -343,19 +344,19 @@ static int do_push(const char *repo) error("failed to push to '%s'", uri[i]); switch (err) { case -ERR_RUN_COMMAND_FORK: - die("unable to fork for %s", sender); + error("unable to fork for %s", sender); case -ERR_RUN_COMMAND_EXEC: - die("unable to exec %s", sender); + error("unable to exec %s", sender); + break; case -ERR_RUN_COMMAND_WAITPID: case -ERR_RUN_COMMAND_WAITPID_WRONG_PID: case -ERR_RUN_COMMAND_WAITPID_SIGNAL: case -ERR_RUN_COMMAND_WAITPID_NOEXIT: - die("%s died with strange error", sender); - default: - return -err; + error("%s died with strange error", sender); } + errs++; } - return 0; + return !!errs; } int cmd_push(int argc, const char **argv, const char *prefix) From 68faf68938ee943fc251c702f2027e4dfda354db Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 15 Feb 2007 16:32:45 -0800 Subject: [PATCH 35/90] A new merge stragety 'subtree'. This merge strategy largely piggy-backs on git-merge-recursive. When merging trees A and B, if B corresponds to a subtree of A, B is first adjusted to match the tree structure of A, instead of reading the trees at the same level. This adjustment is also done to the common ancestor tree. If you are pulling updates from git-gui repository into git.git repository, the root level of the former corresponds to git-gui/ subdirectory of the latter. The tree object of git-gui's toplevel is wrapped in a fake tree object, whose sole entry has name 'git-gui' and records object name of the true tree, before being used by the 3-way merge code. If you are merging the other way, only the git-gui/ subtree of git.git is extracted and merged into git-gui's toplevel. The detection of corresponding subtree is done by comparing the pathnames and types in the toplevel of the tree. Heuristics galore! That's the git way ;-). Signed-off-by: Junio C Hamano --- .gitignore | 2 + Makefile | 10 +- cache.h | 3 + git-merge.sh | 4 +- match-trees.c | 304 +++++++++++++++++++++++++++++++++++++++++++++ merge-recursive.c | 29 +++++ test-match-trees.c | 24 ++++ 7 files changed, 373 insertions(+), 3 deletions(-) create mode 100644 match-trees.c create mode 100644 test-match-trees.c diff --git a/.gitignore b/.gitignore index b39f78fcdf..9229e918cd 100644 --- a/.gitignore +++ b/.gitignore @@ -77,6 +77,7 @@ git-merge-ours git-merge-recursive git-merge-resolve git-merge-stupid +git-merge-subtree git-mergetool git-mktag git-mktree @@ -148,6 +149,7 @@ test-chmtime test-date test-delta test-dump-cache-tree +test-match-trees common-cmds.h *.tar.gz *.dsc diff --git a/Makefile b/Makefile index ac29c629e3..a77d31de98 100644 --- a/Makefile +++ b/Makefile @@ -251,6 +251,8 @@ BUILT_INS = \ # what 'all' will build and 'install' will install, in gitexecdir ALL_PROGRAMS = $(PROGRAMS) $(SCRIPTS) +ALL_PROGRAMS += git-merge-subtree$X + # what 'all' will build but not install in gitexecdir OTHER_PROGRAMS = git$X gitweb/gitweb.cgi ifndef NO_TCLTK @@ -299,7 +301,7 @@ LIB_OBJS = \ server-info.o setup.o sha1_file.o sha1_name.o strbuf.o \ tag.o tree.o usage.o config.o environment.o ctype.o copy.o \ revision.o pager.o tree-walk.o xdiff-interface.o \ - write_or_die.o trace.o list-objects.o grep.o \ + write_or_die.o trace.o list-objects.o grep.o match-trees.o \ alloc.o merge-file.o path-list.o help.o unpack-trees.o $(DIFF_OBJS) \ color.o wt-status.o archive-zip.o archive-tar.o shallow.o utf8.o \ convert.o @@ -725,6 +727,9 @@ git$X: git.c common-cmds.h $(BUILTIN_OBJS) $(GITLIBS) GIT-CFLAGS help.o: common-cmds.h +git-merge-subtree$X: git-merge-recursive$X + rm -f $@ && ln git-merge-recursive$X $@ + $(BUILT_INS): git$X $(QUIET_BUILT_IN)rm -f $@ && ln git$X $@ @@ -942,6 +947,9 @@ test-dump-cache-tree$X: dump-cache-tree.o $(GITLIBS) test-sha1$X: test-sha1.o $(GITLIBS) $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS) +test-match-trees$X: test-match-trees.o $(GITLIBS) + $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS) + test-chmtime$X: test-chmtime.c $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $< diff --git a/cache.h b/cache.h index 1b50c32b13..eb57507b80 100644 --- a/cache.h +++ b/cache.h @@ -496,4 +496,7 @@ extern void trace_argv_printf(const char **argv, int count, const char *format, extern int convert_to_git(const char *path, char **bufp, unsigned long *sizep); extern int convert_to_working_tree(const char *path, char **bufp, unsigned long *sizep); +/* match-trees.c */ +void shift_tree(const unsigned char *, const unsigned char *, unsigned char *, int); + #endif /* CACHE_H */ diff --git a/git-merge.sh b/git-merge.sh index fa4589173f..7ebbce4bdb 100755 --- a/git-merge.sh +++ b/git-merge.sh @@ -16,10 +16,10 @@ test -z "$(git ls-files -u)" || LF=' ' -all_strategies='recur recursive octopus resolve stupid ours' +all_strategies='recur recursive octopus resolve stupid ours subtree' default_twohead_strategies='recursive' default_octopus_strategies='octopus' -no_trivial_merge_strategies='ours' +no_trivial_merge_strategies='ours subtree' use_strategies= index_merge=t diff --git a/match-trees.c b/match-trees.c new file mode 100644 index 0000000000..23cafe47b4 --- /dev/null +++ b/match-trees.c @@ -0,0 +1,304 @@ +#include "cache.h" +#include "tree.h" +#include "tree-walk.h" + +static int score_missing(unsigned mode, const char *path) +{ + int score; + + if (S_ISDIR(mode)) + score = -1000; + else if (S_ISLNK(mode)) + score = -500; + else + score = -50; + return score; +} + +static int score_differs(unsigned mode1, unsigned mode2, const char *path) +{ + int score; + + if (S_ISDIR(mode1) != S_ISDIR(mode2)) + score = -100; + else if (S_ISLNK(mode1) != S_ISLNK(mode2)) + score = -50; + else + score = -5; + return score; +} + +static int score_matches(unsigned mode1, unsigned mode2, const char *path) +{ + int score; + + /* Heh, we found SHA-1 collisions between different kind of objects */ + if (S_ISDIR(mode1) != S_ISDIR(mode2)) + score = -100; + else if (S_ISLNK(mode1) != S_ISLNK(mode2)) + score = -50; + + else if (S_ISDIR(mode1)) + score = 1000; + else if (S_ISLNK(mode1)) + score = 500; + else + score = 250; + return score; +} + +/* + * Inspect two trees, and give a score that tells how similar they are. + */ +static int score_trees(const unsigned char *hash1, const unsigned char *hash2) +{ + struct tree_desc one; + struct tree_desc two; + void *one_buf, *two_buf; + int score = 0; + enum object_type type; + unsigned long size; + + one_buf = read_sha1_file(hash1, &type, &size); + if (!one_buf) + die("unable to read tree (%s)", sha1_to_hex(hash1)); + if (type != OBJ_TREE) + die("%s is not a tree", sha1_to_hex(hash1)); + init_tree_desc(&one, one_buf, size); + two_buf = read_sha1_file(hash2, &type, &size); + if (!two_buf) + die("unable to read tree (%s)", sha1_to_hex(hash2)); + if (type != OBJ_TREE) + die("%s is not a tree", sha1_to_hex(hash2)); + init_tree_desc(&two, two_buf, size); + while (one.size | two.size) { + const unsigned char *elem1 = elem1; + const unsigned char *elem2 = elem2; + const char *path1 = path1; + const char *path2 = path2; + unsigned mode1 = mode1; + unsigned mode2 = mode2; + int cmp; + + if (one.size) + elem1 = tree_entry_extract(&one, &path1, &mode1); + if (two.size) + elem2 = tree_entry_extract(&two, &path2, &mode2); + + if (!one.size) { + /* two has more entries */ + score += score_missing(mode2, path2); + update_tree_entry(&two); + continue; + } + if (!two.size) { + /* two lacks this entry */ + score += score_missing(mode1, path1); + update_tree_entry(&one); + continue; + } + cmp = base_name_compare(path1, strlen(path1), mode1, + path2, strlen(path2), mode2); + if (cmp < 0) { + /* path1 does not appear in two */ + score += score_missing(mode1, path1); + update_tree_entry(&one); + continue; + } + else if (cmp > 0) { + /* path2 does not appear in one */ + score += score_missing(mode2, path2); + update_tree_entry(&two); + continue; + } + else if (hashcmp(elem1, elem2)) + /* they are different */ + score += score_differs(mode1, mode2, path1); + else + /* same subtree or blob */ + score += score_matches(mode1, mode2, path1); + update_tree_entry(&one); + update_tree_entry(&two); + } + free(one_buf); + free(two_buf); + return score; +} + +/* + * Match one itself and its subtrees with two and pick the best match. + */ +static void match_trees(const unsigned char *hash1, + const unsigned char *hash2, + int *best_score, + char **best_match, + char *base, + int recurse_limit) +{ + struct tree_desc one; + void *one_buf; + enum object_type type; + unsigned long size; + + one_buf = read_sha1_file(hash1, &type, &size); + if (!one_buf) + die("unable to read tree (%s)", sha1_to_hex(hash1)); + if (type != OBJ_TREE) + die("%s is not a tree", sha1_to_hex(hash1)); + init_tree_desc(&one, one_buf, size); + + while (one.size) { + const char *path; + const unsigned char *elem; + unsigned mode; + int score; + + elem = tree_entry_extract(&one, &path, &mode); + if (!S_ISDIR(mode)) + goto next; + score = score_trees(elem, hash2); + if (*best_score < score) { + char *newpath; + newpath = xmalloc(strlen(base) + strlen(path) + 1); + sprintf(newpath, "%s%s", base, path); + free(*best_match); + *best_match = newpath; + *best_score = score; + } + if (recurse_limit) { + char *newbase; + newbase = xmalloc(strlen(base) + strlen(path) + 2); + sprintf(newbase, "%s%s/", base, path); + match_trees(elem, hash2, best_score, best_match, + newbase, recurse_limit - 1); + free(newbase); + } + + next: + update_tree_entry(&one); + } + free(one_buf); +} + +/* + * A tree "hash1" has a subdirectory at "prefix". Come up with a + * tree object by replacing it with another tree "hash2". + */ +static int splice_tree(const unsigned char *hash1, + char *prefix, + const unsigned char *hash2, + unsigned char *result) +{ + char *subpath; + int toplen; + char *buf; + unsigned long sz; + struct tree_desc desc; + unsigned char *rewrite_here; + const unsigned char *rewrite_with; + unsigned char subtree[20]; + enum object_type type; + int status; + + subpath = strchr(prefix, '/'); + if (!subpath) + toplen = strlen(prefix); + else { + toplen = subpath - prefix; + subpath++; + } + + buf = read_sha1_file(hash1, &type, &sz); + if (!buf) + die("cannot read tree %s", sha1_to_hex(hash1)); + init_tree_desc(&desc, buf, sz); + + rewrite_here = NULL; + while (desc.size) { + const char *name; + unsigned mode; + const unsigned char *sha1; + + sha1 = tree_entry_extract(&desc, &name, &mode); + if (strlen(name) == toplen && + !memcmp(name, prefix, toplen)) { + if (!S_ISDIR(mode)) + die("entry %s in tree %s is not a tree", + name, sha1_to_hex(hash1)); + rewrite_here = (unsigned char *) sha1; + break; + } + update_tree_entry(&desc); + } + if (!rewrite_here) + die("entry %.*s not found in tree %s", + toplen, prefix, sha1_to_hex(hash1)); + if (subpath) { + status = splice_tree(rewrite_here, subpath, hash2, subtree); + if (status) + return status; + rewrite_with = subtree; + } + else + rewrite_with = hash2; + hashcpy(rewrite_here, rewrite_with); + status = write_sha1_file(buf, sz, tree_type, result); + free(buf); + return status; +} + +/* + * We are trying to come up with a merge between one and two that + * results in a tree shape similar to one. The tree two might + * correspond to a subtree of one, in which case it needs to be + * shifted down by prefixing otherwise empty directories. On the + * other hand, it could cover tree one and we might need to pick a + * subtree of it. + */ +void shift_tree(const unsigned char *hash1, + const unsigned char *hash2, + unsigned char *shifted, + int depth_limit) +{ + char *add_prefix; + char *del_prefix; + int add_score, del_score; + + add_score = del_score = score_trees(hash1, hash2); + add_prefix = xcalloc(1, 1); + del_prefix = xcalloc(1, 1); + + /* + * See if one's subtree resembles two; if so we need to prefix + * two with a few fake trees to match the prefix. + */ + match_trees(hash1, hash2, &add_score, &add_prefix, "", depth_limit); + + /* + * See if two's subtree resembles one; if so we need to + * pick only subtree of two. + */ + match_trees(hash2, hash1, &del_score, &del_prefix, "", depth_limit); + + /* Assume we do not have to do any shifting */ + hashcpy(shifted, hash2); + + if (add_score < del_score) { + /* We need to pick a subtree of two */ + unsigned mode; + + if (!*del_prefix) + return; + + if (get_tree_entry(hash2, del_prefix, shifted, &mode)) + die("cannot find path %s in tree %s", + del_prefix, sha1_to_hex(hash2)); + return; + } + + if (!*add_prefix) + return; + + splice_tree(hash1, add_prefix, hash2, shifted); +} + diff --git a/merge-recursive.c b/merge-recursive.c index 2b614b64ba..3096594b3e 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -16,6 +16,22 @@ #include "path-list.h" #include "xdiff-interface.h" +static int subtree_merge; + +static struct tree *shift_tree_object(struct tree *one, struct tree *two) +{ + unsigned char shifted[20]; + + /* + * NEEDSWORK: this limits the recursion depth to hardcoded + * value '2' to avoid excessive overhead. + */ + shift_tree(one->object.sha1, two->object.sha1, shifted, 2); + if (!hashcmp(two->object.sha1, shifted)) + return two; + return lookup_tree(shifted); +} + /* * A virtual commit has * - (const char *)commit->util set to the name, and @@ -1137,6 +1153,12 @@ static int merge_trees(struct tree *head, struct tree **result) { int code, clean; + + if (subtree_merge) { + merge = shift_tree_object(head, merge); + common = shift_tree_object(head, common); + } + if (sha_eq(common->object.sha1, merge->object.sha1)) { output(0, "Already uptodate!"); *result = head; @@ -1342,6 +1364,13 @@ int main(int argc, char *argv[]) struct lock_file *lock = xcalloc(1, sizeof(struct lock_file)); int index_fd; + if (argv[0]) { + int namelen = strlen(argv[0]); + if (8 < namelen && + !strcmp(argv[0] + namelen - 8, "-subtree")) + subtree_merge = 1; + } + git_config(merge_config); if (getenv("GIT_MERGE_VERBOSITY")) verbosity = strtol(getenv("GIT_MERGE_VERBOSITY"), NULL, 10); diff --git a/test-match-trees.c b/test-match-trees.c new file mode 100644 index 0000000000..a3c4688778 --- /dev/null +++ b/test-match-trees.c @@ -0,0 +1,24 @@ +#include "cache.h" +#include "tree.h" + +int main(int ac, char **av) +{ + unsigned char hash1[20], hash2[20], shifted[20]; + struct tree *one, *two; + + if (get_sha1(av[1], hash1)) + die("cannot parse %s as an object name", av[1]); + if (get_sha1(av[2], hash2)) + die("cannot parse %s as an object name", av[2]); + one = parse_tree_indirect(hash1); + if (!one) + die("not a treeish %s", av[1]); + two = parse_tree_indirect(hash2); + if (!two) + die("not a treeish %s", av[2]); + + shift_tree(one->object.sha1, two->object.sha1, shifted, -1); + printf("shifted: %s\n", sha1_to_hex(shifted)); + + exit(0); +} From e94a4f6eed31d652b94e8cccca8636d824ff2622 Mon Sep 17 00:00:00 2001 From: Frank Lichtenheld Date: Sat, 7 Apr 2007 16:58:08 +0200 Subject: [PATCH 36/90] cvsserver: small corrections to asciidoc documentation Fix a typo: s/Not/Note/ Some formating fixes: Use ` ` syntax for all filenames and ' ' syntax for all commandline switches. Signed-off-by: Frank Lichtenheld Signed-off-by: Junio C Hamano --- Documentation/git-cvsserver.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Documentation/git-cvsserver.txt b/Documentation/git-cvsserver.txt index 85d0950cf4..f9e0c77379 100644 --- a/Documentation/git-cvsserver.txt +++ b/Documentation/git-cvsserver.txt @@ -110,12 +110,12 @@ To get a checkout with the Eclipse CVS client: Protocol notes: If you are using anonymous access via pserver, just select that. Those using SSH access should choose the 'ext' protocol, and configure 'ext' access on the Preferences->Team->CVS->ExtConnection pane. Set CVS_SERVER to -'git-cvsserver'. Not that password support is not good when using 'ext', +'git-cvsserver'. Note that password support is not good when using 'ext', you will definitely want to have SSH keys setup. Alternatively, you can just use the non-standard extssh protocol that Eclipse offer. In that case CVS_SERVER is ignored, and you will have to replace -the cvs utility on the server with git-cvsserver or manipulate your .bashrc +the cvs utility on the server with git-cvsserver or manipulate your `.bashrc` so that calling 'cvs' effectively calls git-cvsserver. Clients known to work @@ -134,9 +134,9 @@ checkout, diff, status, update, log, add, remove, commit. Legacy monitoring operations are not supported (edit, watch and related). Exports and tagging (tags and branches) are not supported at this stage. -The server should set the -k mode to binary when relevant, however, +The server should set the '-k' mode to binary when relevant, however, this is not really implemented yet. For now, you can force the server -to set `-kb` for all files by setting the `gitcvs.allbinary` config +to set '-kb' for all files by setting the `gitcvs.allbinary` config variable. In proper GIT tradition, the contents of the files are always respected. No keyword expansion or newline munging is supported. From 732bcf942c6e99879349711fd29b5062a704e248 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 7 Apr 2007 23:33:14 -0700 Subject: [PATCH 37/90] Prepare for 1.5.1.1 Signed-off-by: Junio C Hamano --- Documentation/RelNotes-1.5.1.1.txt | 46 ++++++++++++++++++++++++++++++ RelNotes | 2 +- 2 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 Documentation/RelNotes-1.5.1.1.txt diff --git a/Documentation/RelNotes-1.5.1.1.txt b/Documentation/RelNotes-1.5.1.1.txt new file mode 100644 index 0000000000..b48b4bc3e8 --- /dev/null +++ b/Documentation/RelNotes-1.5.1.1.txt @@ -0,0 +1,46 @@ +GIT v1.5.1.1 Release Notes (draft) +========================== + +Fixes since v1.5.1 +------------------ + +* Documentation updates + + - The --left-right option of rev-list and friends is documented. + + - The documentation for cvsimport has been majorly improved. + +* Bugfixes + + - "git send-email" produced of References header of unbounded length; + fixed this with line-folding. + + - "git archive" to download from remote site should not + require you to be in a git repository, but it incorrectly + did. + + - "git apply" ignored -p for "diff --git" formatted + patches. + + - "git rerere" recorded a conflict that had one side empty + (the other side adds) incorrectly; this made merging in the + other direction fail to use previously recorded resolution. + + - t4200 test was broken where "wc -l" pads its output with + spaces. + + - "git branch -m old new" to rename branch did not work + without a configuration file in ".git/config". + + - The sample hook for notification e-mail was misnamed. + + - gitweb did not show type-changing patch correctly in the + blobdiff view. + +* Performance Tweaks + +-- +exec >/var/tmp/1 +O=v1.5.1-26-ge94a4f6 +echo O=`git describe refs/heads/maint` +git shortlog --no-merges $O..refs/heads/maint diff --git a/RelNotes b/RelNotes index d5e055de6f..59e7391e0f 120000 --- a/RelNotes +++ b/RelNotes @@ -1 +1 @@ -Documentation/RelNotes-1.5.1.txt \ No newline at end of file +Documentation/RelNotes-1.5.1.1.txt \ No newline at end of file From 8d1608b8bf49d6dfbc843b981c2dfb730fc3a96b Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 7 Apr 2007 23:59:32 -0700 Subject: [PATCH 38/90] Start 1.5.2 cycle by prepareing RelNotes for it. Signed-off-by: Junio C Hamano --- Documentation/RelNotes-1.5.2.txt | 76 ++++++++++++++++++++++++++++++++ RelNotes | 2 +- 2 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 Documentation/RelNotes-1.5.2.txt diff --git a/Documentation/RelNotes-1.5.2.txt b/Documentation/RelNotes-1.5.2.txt new file mode 100644 index 0000000000..2e3c7bc4f1 --- /dev/null +++ b/Documentation/RelNotes-1.5.2.txt @@ -0,0 +1,76 @@ +GIT v1.5.2 Release Notes (draft) +======================== + +Updates since v1.5.1 +-------------------- + +* New commands and options. + + - "git bisect start" can optionally take a single bad commit and + zero or more good commits on the command line. + +* Updated behavior of existing commands. + + - "git diff --stat" shows size of preimage and postimage blobs + for binary contents. Earlier it only said "Bin". + + - "git lost-found" shows stuff that are unreachable except + from reflogs. + + - "git checkout branch^0" now detaches HEAD at the tip commit + on the named branch, instead of just switching to the + branch (use "git checkout branch" to switch to the branch, + as before). + + - "git bisect next" can be used after giving only a bad commit + without giving a good one (this starts bisection half-way to + the root commit). We used to refuse to operate without a + good and a bad commit. + +* Builds + + - git-p4import has never been installed; now there is an + installation option to do so. + + - gitk and git-gui can be configured out. + + - Generated documentation pages automatically get version + information from GIT_VERSION + + - Parallel build with "make -j" descending into subdirectory + was fixed. + +* Performance Tweaks + + - optimized "git-rev-list --bisect" (hence "git-bisect"). + + - optimized "git-add $path" in a large directory, most of + whose contents are ignored. + + +Fixes since v1.5.1 +------------------ + +The following are all in v1.5.1.x series, unless otherwise noted. + +* Documentation updates + +* Bugfixes + + - Switching branches with "git checkout" refused to work when + a path changes from a file to a directory between the + current branch and the new branch, in order not to lose + possible local changes in the directory that is being turned + into a file with the switch. We now allow such a branch + switch after making sure that there is no locally modified + file nor un-ignored file in the directory. This has not + been backported to 1.5.1.x series, as it is rather an + intrusive change. + +* Performance Tweaks + +-- +exec >/var/tmp/1 +O=v1.5.1-91-g640ee0d +echo O=`git describe refs/heads/master` +git shortlog --no-merges $O..refs/heads/master ^refs/heads/maint diff --git a/RelNotes b/RelNotes index d5e055de6f..c543b1d1ee 120000 --- a/RelNotes +++ b/RelNotes @@ -1 +1 @@ -Documentation/RelNotes-1.5.1.txt \ No newline at end of file +Documentation/RelNotes-1.5.2.txt \ No newline at end of file From 512b620bd9fef7f170562ecad835e37479f051ce Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Tue, 3 Apr 2007 01:57:08 -0700 Subject: [PATCH 39/90] git-svn: bail out on incorrect command-line options "git svn log" is the only command that needs the pass-through option in Getopt::Long; otherwise we will bail out and let the user know something is wrong. Also, avoid printing out unaccepted mixed-case options (that are reserved for the command-line) such as --useSvmProps in the usage() function. Signed-off-by: Eric Wong Signed-off-by: Junio C Hamano --- git-svn.perl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/git-svn.perl b/git-svn.perl index d307d430f3..6216cade0f 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -33,7 +33,7 @@ use Carp qw/croak/; use IO::File qw//; use File::Basename qw/dirname basename/; use File::Path qw/mkpath/; -use Getopt::Long qw/:config gnu_getopt no_ignore_case auto_abbrev pass_through/; +use Getopt::Long qw/:config gnu_getopt no_ignore_case auto_abbrev/; use IPC::Open3; use Git; @@ -168,6 +168,7 @@ for (my $i = 0; $i < @ARGV; $i++) { my %opts = %{$cmd{$cmd}->[2]} if (defined $cmd); read_repo_config(\%opts); +Getopt::Long::Configure('pass_through') if $cmd eq 'log'; my $rv = GetOptions(%opts, 'help|H|h' => \$_help, 'version|V' => \$_version, 'minimize-connections' => \$Git::SVN::Migration::_minimize, 'id|i=s' => \$Git::SVN::default_ref_id, @@ -229,6 +230,8 @@ Usage: $0 [options] [arguments]\n next if /^multi-/; # don't show deprecated commands print $fd ' ',pack('A17',$_),$cmd{$_}->[1],"\n"; foreach (keys %{$cmd{$_}->[2]}) { + # mixed-case options are for .git/config only + next if /[A-Z]/ && /^[a-z]+$/i; # prints out arguments as they should be passed: my $x = s#[:=]s$## ? '' : s#[:=]i$## ? '' : ''; print $fd ' ' x 21, join(', ', map { length $_ > 1 ? From 13c823fb520eaf1cded520213cf0ae4c3268208d Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Sun, 8 Apr 2007 00:59:19 -0700 Subject: [PATCH 40/90] git-svn: dcommit/rebase confused by patches with git-svn-id: lines When patches are merged from another git-svn managed branch, they will have the git-svn-id: metadata line in them (generated by git-format-patch). When doing rebase or dcommit via git-svn, this would cause git-svn to find the wrong upstream branch. We now verify that the commit is consistent with the value in the .rev_db file. Signed-off-by: Eric Wong Signed-off-by: Junio C Hamano --- git-svn.perl | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/git-svn.perl b/git-svn.perl index 6216cade0f..e4079fb76c 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -363,13 +363,12 @@ sub cmd_dcommit { my $head = shift; $head ||= 'HEAD'; my @refs; - my ($url, $rev, $uuid) = working_head_info($head, \@refs); - my $c = $refs[-1]; - unless (defined $url && defined $rev && defined $uuid) { + my ($url, $rev, $uuid, $gs) = working_head_info($head, \@refs); + unless ($gs) { die "Unable to determine upstream SVN information from ", "$head history\n"; } - my $gs = Git::SVN->find_by_url($url); + my $c = $refs[-1]; my $last_rev; foreach my $d (@refs) { if (!verify_ref("$d~1")) { @@ -431,16 +430,11 @@ sub cmd_dcommit { sub cmd_rebase { command_noisy(qw/update-index --refresh/); - my $url = (working_head_info('HEAD'))[0]; - if (!defined $url) { + my ($url, $rev, $uuid, $gs) = working_head_info('HEAD'); + unless ($gs) { die "Unable to determine upstream SVN information from ", "working tree history\n"; } - - my $gs = Git::SVN->find_by_url($url); - unless ($gs) { - die "Unable to determine remote information from URL: $url\n"; - } if (command(qw/diff-index HEAD --/)) { print STDERR "Cannot rebase with uncommited changes:\n"; command_noisy('status'); @@ -453,8 +447,8 @@ sub cmd_rebase { } sub cmd_show_ignore { - my $url = (::working_head_info('HEAD'))[0]; - my $gs = Git::SVN->find_by_url($url) || Git::SVN->new; + my ($url, $rev, $uuid, $gs) = working_head_info('HEAD'); + $gs ||= Git::SVN->new; my $r = (defined $_revision ? $_revision : $gs->ra->get_latest_revnum); $gs->traverse_ignore(\*STDOUT, $gs->{path}, $r); } @@ -776,16 +770,23 @@ sub cmt_metadata { sub working_head_info { my ($head, $refs) = @_; - my ($url, $rev, $uuid); my ($fh, $ctx) = command_output_pipe('rev-list', $head); while (<$fh>) { chomp; - ($url, $rev, $uuid) = cmt_metadata($_); - last if (defined $url && defined $rev && defined $uuid); + my ($url, $rev, $uuid) = cmt_metadata($_); + if (defined $url && defined $rev) { + if (my $gs = Git::SVN->find_by_url($url)) { + my $c = $gs->rev_db_get($rev); + if ($c && $c eq $_) { + close $fh; # break the pipe + return ($url, $rev, $uuid, $gs); + } + } + } unshift @$refs, $_ if $refs; } - close $fh; # break the pipe - ($url, $rev, $uuid); + command_close_pipe($fh, $ctx); + (undef, undef, undef, undef); } package Git::SVN; @@ -3321,8 +3322,8 @@ sub git_svn_log_cmd { last; } - my $url = (::working_head_info($head))[0]; - my $gs = Git::SVN->find_by_url($url) || Git::SVN->_new; + my ($url, $rev, $uuid, $gs) = ::working_head_info($head); + $gs ||= Git::SVN->_new; my @cmd = (qw/log --abbrev-commit --pretty=raw --default/, $gs->refname); push @cmd, '-r' unless $non_recursive; From c16d08713e2d7f5c440f7649ceb68f838b4b8bea Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Sun, 8 Apr 2007 00:59:22 -0700 Subject: [PATCH 41/90] git-svn: fix log command to avoid infinite loop on long commit messages This bug has been around since the the conversion to use the Git.pm library back in October or November. Eventually I'd like "git rev-list/log" to have the option to not truncate overly long messages. Signed-off-by: Eric Wong Signed-off-by: Junio C Hamano --- git-svn.perl | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/git-svn.perl b/git-svn.perl index e4079fb76c..ac44f60b81 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -3263,12 +3263,19 @@ my $l_fmt; sub cmt_showable { my ($c) = @_; return 1 if defined $c->{r}; + + # big commit message got truncated by the 16k pretty buffer in rev-list if ($c->{l} && $c->{l}->[-1] eq "...\n" && $c->{a_raw} =~ /\@([a-f\d\-]+)>$/) { + @{$c->{l}} = (); my @log = command(qw/cat-file commit/, $c->{c}); - shift @log while ($log[0] ne "\n"); + + # shift off the headers + shift @log while ($log[0] ne ''); shift @log; - @{$c->{l}} = grep !/^git-svn-id: /, @log; + + # TODO: make $c->{l} not have a trailing newline in the future + @{$c->{l}} = map { "$_\n" } grep !/^git-svn-id: /, @log; (undef, $c->{r}, undef) = ::extract_metadata( (grep(/^git-svn-id: /, @log))[-1]); From 24c64d6add05f1f4d2e277af2d44c961910c98d3 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 8 Apr 2007 22:14:16 -0700 Subject: [PATCH 42/90] Add Documentation/cmd-list.made to .gitignore Noticed by Randal L. Schwartz. Signed-off-by: Junio C Hamano --- Documentation/.gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/.gitignore b/Documentation/.gitignore index 6a51331b2f..b98d21e98e 100644 --- a/Documentation/.gitignore +++ b/Documentation/.gitignore @@ -2,6 +2,7 @@ *.html *.1 *.7 +*.made howto-index.txt doc.dep cmds-*.txt From 8ff21b1a3307c7059ea1e00b5117a50a2bc5fec8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Mon, 9 Apr 2007 17:12:53 +0200 Subject: [PATCH 43/90] git-archive: make tar the default format As noted by Junio, --format=tar should be assumed if no format was specified. Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- Documentation/git-archive.txt | 3 ++- builtin-archive.c | 4 +--- t/t5000-tar-tree.sh | 10 +++++++++- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/Documentation/git-archive.txt b/Documentation/git-archive.txt index 493474b2ee..8d1041598e 100644 --- a/Documentation/git-archive.txt +++ b/Documentation/git-archive.txt @@ -30,7 +30,8 @@ OPTIONS ------- --format=:: - Format of the resulting archive: 'tar', 'zip'... + Format of the resulting archive: 'tar', 'zip'... The default + is 'tar'. --list:: Show all available formats. diff --git a/builtin-archive.c b/builtin-archive.c index 8ea6cb1efc..7f4e409c99 100644 --- a/builtin-archive.c +++ b/builtin-archive.c @@ -149,7 +149,7 @@ int parse_archive_args(int argc, const char **argv, struct archiver *ar) { const char *extra_argv[MAX_EXTRA_ARGS]; int extra_argc = 0; - const char *format = NULL; /* might want to default to "tar" */ + const char *format = "tar"; const char *base = ""; int verbose = 0; int i; @@ -190,8 +190,6 @@ int parse_archive_args(int argc, const char **argv, struct archiver *ar) /* We need at least one parameter -- tree-ish */ if (argc - 1 < i) usage(archive_usage); - if (!format) - die("You must specify an archive format"); if (init_archiver(format, ar) < 0) die("Unknown archive format '%s'", format); diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh index b4359df795..e223c074f0 100755 --- a/t/t5000-tar-tree.sh +++ b/t/t5000-tar-tree.sh @@ -49,9 +49,17 @@ test_expect_success \ git-update-ref HEAD $(TZ=GMT GIT_COMMITTER_DATE="2005-05-27 22:00:00" \ git-commit-tree $treeid b.tar' + test_expect_success \ 'git-tar-tree' \ - 'git-tar-tree HEAD >b.tar' + 'git-tar-tree HEAD >b2.tar' + +test_expect_success \ + 'git-archive vs. git-tar-tree' \ + 'diff b.tar b2.tar' test_expect_success \ 'validate file modification time' \ From fc1f458c3506ea3f76a328f10b4008c45af32c4b Mon Sep 17 00:00:00 2001 From: Tomash Brechko Date: Mon, 9 Apr 2007 15:24:02 +0400 Subject: [PATCH 44/90] cvsexportcommit -p : fix the usage of git-apply -C. Unlike 'patch --fuzz=NUM', which specifies the number of lines allowed to mismatch, 'git-apply -CNUM' requests the match of NUM lines of context. Omitting -C requests full context match, and that's what should be used for cvsexportcommit -p. Signed-off-by: Junio C Hamano --- git-cvsexportcommit.perl | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/git-cvsexportcommit.perl b/git-cvsexportcommit.perl index 67224b4449..6ed471918d 100755 --- a/git-cvsexportcommit.perl +++ b/git-cvsexportcommit.perl @@ -124,12 +124,17 @@ close MSG; `git-diff-tree --binary -p $parent $commit >.cvsexportcommit.diff`;# || die "Cannot diff"; ## apply non-binary changes -my $fuzz = $opt_p ? 0 : 2; + +# In pedantic mode require all lines of context to match. In normal +# mode, be compatible with diff/patch: assume 3 lines of context and +# require at least one line match, i.e. ignore at most 2 lines of +# context, like diff/patch do by default. +my $context = $opt_p ? '' : '-C1'; print "Checking if patch will apply\n"; my @stat; -open APPLY, "GIT_DIR= git-apply -C$fuzz --binary --summary --numstat<.cvsexportcommit.diff|" || die "cannot patch"; +open APPLY, "GIT_DIR= git-apply $context --binary --summary --numstat<.cvsexportcommit.diff|" || die "cannot patch"; @stat=; close APPLY || die "Cannot patch"; my (@bfiles,@files,@afiles,@dfiles); @@ -196,7 +201,7 @@ if ($dirty) { } print "Applying\n"; -`GIT_DIR= git-apply -C$fuzz --binary --summary --numstat --apply <.cvsexportcommit.diff` || die "cannot patch"; +`GIT_DIR= git-apply $context --binary --summary --numstat --apply <.cvsexportcommit.diff` || die "cannot patch"; print "Patch applied successfully. Adding new files and directories to CVS\n"; my $dirtypatch = 0; From 8bd26c4a2f7539f6418934858710c674bb139cdd Mon Sep 17 00:00:00 2001 From: Julian Phillips Date: Mon, 9 Apr 2007 21:57:36 +0100 Subject: [PATCH 45/90] Documentation: show-ref: document --exclude-existing Use the comment in the code to document the --exclude-existing function to git-show-ref. Signed-off-by: Julian Phillips Signed-off-by: Junio C Hamano --- Documentation/git-show-ref.txt | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Documentation/git-show-ref.txt b/Documentation/git-show-ref.txt index 5973a82517..2355aa5e86 100644 --- a/Documentation/git-show-ref.txt +++ b/Documentation/git-show-ref.txt @@ -10,6 +10,7 @@ SYNOPSIS [verse] 'git-show-ref' [-q|--quiet] [--verify] [-h|--head] [-d|--dereference] [-s|--hash] [--abbrev] [--tags] [--heads] [--] ... +'git-show-ref' --exclude-existing[=pattern] DESCRIPTION ----------- @@ -19,6 +20,9 @@ commit IDs. Results can be filtered using a pattern and tags can be dereferenced into object IDs. Additionally, it can be used to test whether a particular ref exists. +The --exclude-existing form is a filter that does the inverse, it shows the +refs from stdin that don't exist in the local repository. + Use of this utility is encouraged in favor of directly accessing files under in the `.git` directory. @@ -61,6 +65,18 @@ OPTIONS Do not print any results to stdout. When combined with '--verify' this can be used to silently check if a reference exists. +--exclude-existing, --exclude-existing=pattern:: + + Make git-show-ref act as a filter that reads refs from stdin of the + form "^(?:\s)?(?:\^\{\})?$" and performs the + following actions on each: + (1) strip "^{}" at the end of line if any; + (2) ignore if pattern is provided and does not head-match refname; + (3) warn if refname is not a well-formed refname and skip; + (4) ignore if refname is a ref that exists in the local repository; + (5) otherwise output the line. + + :: Show references matching one or more patterns. From c2b8b13494f3de6bd9a22162973e4df32665a966 Mon Sep 17 00:00:00 2001 From: Frank Lichtenheld Date: Fri, 6 Apr 2007 23:58:11 +0200 Subject: [PATCH 46/90] gitweb: Allow forks with project list file Make it possible to use the forks feature even when reading the list of projects from a file, by creating a list of known prefixes as we go. Forks have to be listed after the main project in order to be recognised as such. Signed-off-by: Frank Lichtenheld Acked-by: Petr Baudis Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index e49eb91d69..01a10b320e 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -176,8 +176,8 @@ our %feature = ( # projects matching $projname/*.git will not be shown in the main # projects list, instead a '+' mark will be added to $projname # there and a 'forks' view will be enabled for the project, listing - # all the forks. This feature is supported only if project list - # is taken from a directory, not file. + # all the forks. If project list is taken from a file, forks have + # to be listed after the main project. # To enable system wide have in $GITWEB_CONFIG # $feature{'forks'}{'default'} = [1]; @@ -1047,6 +1047,8 @@ sub git_get_projects_list { $filter ||= ''; $filter =~ s/\.git$//; + my ($check_forks) = gitweb_check_feature('forks'); + if (-d $projects_list) { # search in directory my $dir = $projects_list . ($filter ? "/$filter" : ''); @@ -1054,8 +1056,6 @@ sub git_get_projects_list { $dir =~ s!/+$!!; my $pfxlen = length("$dir"); - my ($check_forks) = gitweb_check_feature('forks'); - File::Find::find({ follow_fast => 1, # follow symbolic links dangling_symlinks => 0, # ignore dangling symlinks, silently @@ -1081,7 +1081,9 @@ sub git_get_projects_list { # 'git%2Fgit.git Linus+Torvalds' # 'libs%2Fklibc%2Fklibc.git H.+Peter+Anvin' # 'linux%2Fhotplug%2Fudev.git Greg+Kroah-Hartman' + my %paths; open my ($fd), $projects_list or return; + PROJECT: while (my $line = <$fd>) { chomp $line; my ($path, $owner) = split ' ', $line; @@ -1094,11 +1096,27 @@ sub git_get_projects_list { # looking for forks; my $pfx = substr($path, 0, length($filter)); if ($pfx ne $filter) { - next; + next PROJECT; } my $sfx = substr($path, length($filter)); if ($sfx !~ /^\/.*\.git$/) { - next; + next PROJECT; + } + } elsif ($check_forks) { + PATH: + foreach my $filter (keys %paths) { + # looking for forks; + my $pfx = substr($path, 0, length($filter)); + if ($pfx ne $filter) { + next PATH; + } + my $sfx = substr($path, length($filter)); + if ($sfx !~ /^\/.*\.git$/) { + next PATH; + } + # is a fork, don't include it in + # the list + next PROJECT; } } if (check_export_ok("$projectroot/$path")) { @@ -1106,7 +1124,9 @@ sub git_get_projects_list { path => $path, owner => to_utf8($owner), }; - push @list, $pr + push @list, $pr; + (my $forks_path = $path) =~ s/\.git$//; + $paths{$forks_path}++; } } close $fd; From b06dcf8cb83a9b7655bad5f0f6d84e68fc78beb3 Mon Sep 17 00:00:00 2001 From: Frank Lichtenheld Date: Fri, 6 Apr 2007 23:58:24 +0200 Subject: [PATCH 47/90] gitweb: Allow configuring the default projects order and add order 'none' Introduce new configuration variable $default_projects_order that can be used to specify the default order of projects on the index page if no 'o' parameter is given. Allow a new value 'none' for order that will cause the projects to be in the order we learned about them. In case of reading the list of projects from a file, this should be the order as they are listed in the file. In case of reading the list of projects from a directory this will probably give random results depending on the filesystem in use. Signed-off-by: Frank Lichtenheld Acked-by: Petr Baudis Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 01a10b320e..c48b35aa39 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -71,6 +71,10 @@ our $logo_label = "git homepage"; # source of projects list our $projects_list = "++GITWEB_LIST++"; +# default order of projects list +# valid values are none, project, descr, owner, and age +our $default_projects_order = "project"; + # show repository only if this file exists # (only effective if this variable evaluates to true) our $export_ok = "++GITWEB_EXPORT_OK++"; @@ -1131,7 +1135,6 @@ sub git_get_projects_list { } close $fd; } - @list = sort {$a->{'path'} cmp $b->{'path'}} @list; return @list; } @@ -2618,7 +2621,7 @@ sub git_project_list_body { push @projects, $pr; } - $order ||= "project"; + $order ||= $default_projects_order; $from = 0 unless defined $from; $to = $#projects if (!defined $to || $#projects < $to); @@ -2977,7 +2980,7 @@ sub git_search_grep_body { sub git_project_list { my $order = $cgi->param('o'); - if (defined $order && $order !~ m/project|descr|owner|age/) { + if (defined $order && $order !~ m/none|project|descr|owner|age/) { die_error(undef, "Unknown order parameter"); } @@ -3000,7 +3003,7 @@ sub git_project_list { sub git_forks { my $order = $cgi->param('o'); - if (defined $order && $order !~ m/project|descr|owner|age/) { + if (defined $order && $order !~ m/none|project|descr|owner|age/) { die_error(undef, "Unknown order parameter"); } From f98157720239f0dd94d03632dc873e3eb3135e2f Mon Sep 17 00:00:00 2001 From: Jim Meyering Date: Tue, 10 Apr 2007 00:56:33 +0200 Subject: [PATCH 48/90] (encode_85, decode_85): Mark source buffer pointer as "const". Signed-off-by: Jim Meyering Signed-off-by: Junio C Hamano --- base85.c | 4 ++-- cache.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/base85.c b/base85.c index a9e97f89d9..a6c41d597a 100644 --- a/base85.c +++ b/base85.c @@ -37,7 +37,7 @@ static void prep_base85(void) } } -int decode_85(char *dst, char *buffer, int len) +int decode_85(char *dst, const char *buffer, int len) { prep_base85(); @@ -82,7 +82,7 @@ int decode_85(char *dst, char *buffer, int len) return 0; } -void encode_85(char *buf, unsigned char *data, int bytes) +void encode_85(char *buf, const unsigned char *data, int bytes) { prep_base85(); diff --git a/cache.h b/cache.h index 384b260227..1904131f27 100644 --- a/cache.h +++ b/cache.h @@ -468,8 +468,8 @@ extern int pager_in_use; extern int pager_use_color; /* base85 */ -int decode_85(char *dst, char *line, int linelen); -void encode_85(char *buf, unsigned char *data, int bytes); +int decode_85(char *dst, const char *line, int linelen); +void encode_85(char *buf, const unsigned char *data, int bytes); /* alloc.c */ struct blob; From 8eb2d0bee8813ac37dc602d12b0abba632e96152 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 11 Apr 2007 01:28:43 -0700 Subject: [PATCH 49/90] fsck: do not complain on detached HEAD. Detached HEAD is just a normal state of a repository. Do not say anything about it. Do not give worrying "error:" messages when we let the user know that the HEAD points at nothing (i.e. yet to be born branch), nor we do not have any default refs to start following the objects chain. Reword them as "notice:". Signed-off-by: Junio C Hamano --- builtin-fsck.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/builtin-fsck.c b/builtin-fsck.c index 21f1f9e91d..7c3b0a535f 100644 --- a/builtin-fsck.c +++ b/builtin-fsck.c @@ -532,7 +532,7 @@ static void get_default_heads(void) * "show_unreachable" flag. */ if (!default_refs) { - error("No default references"); + fprintf(stderr, "notice: No default references\n"); show_unreachable = 0; } } @@ -552,15 +552,23 @@ static int fsck_head_link(void) { unsigned char sha1[20]; int flag; - const char *head_points_at = resolve_ref("HEAD", sha1, 1, &flag); + int null_is_error = 0; + const char *head_points_at = resolve_ref("HEAD", sha1, 0, &flag); - if (!head_points_at || !(flag & REF_ISSYMREF)) - return error("HEAD is not a symbolic ref"); - if (prefixcmp(head_points_at, "refs/heads/")) + if (!head_points_at) + return error("Invalid HEAD"); + if (!strcmp(head_points_at, "HEAD")) + /* detached HEAD */ + null_is_error = 1; + else if (prefixcmp(head_points_at, "refs/heads/")) return error("HEAD points to something strange (%s)", head_points_at); - if (is_null_sha1(sha1)) - return error("HEAD: not a valid git pointer"); + if (is_null_sha1(sha1)) { + if (null_is_error) + return error("HEAD: detached HEAD points at nothing"); + fprintf(stderr, "notice: HEAD points to an unborn branch (%s)\n", + head_points_at + 11); + } return 0; } From cb52d9a1fbb2298d73bfa5dc86a0155cc462ecf2 Mon Sep 17 00:00:00 2001 From: Frank Lichtenheld Date: Wed, 11 Apr 2007 22:38:19 +0200 Subject: [PATCH 50/90] cvsserver: Fix handling of diappeared files on update Only send a modified response if the client sent a "Modified" entry. This fixes the case where the file was locally deleted on the client without being removed from CVS. In this case the client will only have sent the Entry for the file but nothing else. Signed-off-by: Frank Lichtenheld Acked-by: Martin Langhoff Acked-by: Daniel Barkalow Signed-off-by: Junio C Hamano --- git-cvsserver.perl | 1 + 1 file changed, 1 insertion(+) diff --git a/git-cvsserver.perl b/git-cvsserver.perl index 68aa75255e..25816c5a21 100755 --- a/git-cvsserver.perl +++ b/git-cvsserver.perl @@ -843,6 +843,7 @@ sub req_update if ( defined ( $wrev ) and defined($meta->{revision}) and $wrev == $meta->{revision} + and defined($state->{entries}{$filename}{modified_hash}) and not exists ( $state->{opt}{C} ) ) { $log->info("Tell the client the file is modified"); From 9b11d24d4156e0b2246383faad16eb8aa0caeefb Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 11 Apr 2007 14:39:07 -0700 Subject: [PATCH 51/90] GIT 1.5.1.1 Signed-off-by: Junio C Hamano --- Documentation/RelNotes-1.5.1.1.txt | 29 ++++++++++++++++++++++------- GIT-VERSION-GEN | 2 +- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/Documentation/RelNotes-1.5.1.1.txt b/Documentation/RelNotes-1.5.1.1.txt index b48b4bc3e8..3054b5a3f0 100644 --- a/Documentation/RelNotes-1.5.1.1.txt +++ b/Documentation/RelNotes-1.5.1.1.txt @@ -1,4 +1,4 @@ -GIT v1.5.1.1 Release Notes (draft) +GIT v1.5.1.1 Release Notes ========================== Fixes since v1.5.1 @@ -10,8 +10,23 @@ Fixes since v1.5.1 - The documentation for cvsimport has been majorly improved. + - "git-show-ref --exclude-existing" was documented. + * Bugfixes + - The implementation of -p option in "git cvsexportcommit" had + the meaning of -C (context reduction) option wrong, and + loosened the context requirements when it was told to be + strict. + + - "git cvsserver" did not behave like the real cvsserver when + client side removed a file from the working tree without + doing anything else on the path. In such a case, it should + restore it from the checked out revision. + + - "git fsck" issued an alarming error message on detached + HEAD. It is not an error since at least 1.5.0. + - "git send-email" produced of References header of unbounded length; fixed this with line-folding. @@ -37,10 +52,10 @@ Fixes since v1.5.1 - gitweb did not show type-changing patch correctly in the blobdiff view. -* Performance Tweaks + - git-svn did not error out with incorrect command line options. --- -exec >/var/tmp/1 -O=v1.5.1-26-ge94a4f6 -echo O=`git describe refs/heads/maint` -git shortlog --no-merges $O..refs/heads/maint + - git-svn fell into an infinite loop when insanely long commit + message was found. + + - git-svn dcommit and rebase was confused by patches that were + merged from another branch that is managed by git-svn. diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN index 48715012be..2325660ff4 100755 --- a/GIT-VERSION-GEN +++ b/GIT-VERSION-GEN @@ -1,7 +1,7 @@ #!/bin/sh GVF=GIT-VERSION-FILE -DEF_VER=v1.5.1.GIT +DEF_VER=v1.5.1.1.GIT LF=' ' From 2d9e4a47d16e9d2100cc88ef6126aa7619be51ed Mon Sep 17 00:00:00 2001 From: "Robin H. Johnson" Date: Wed, 11 Apr 2007 16:58:07 -0700 Subject: [PATCH 52/90] Add custom subject prefix support to format-patch (take 3) Add a new option to git-format-patch, entitled --subject-prefix that allows control of the subject prefix '[PATCH]'. Using this option, the text 'PATCH' is replaced with whatever input is provided to the option. This allows easily generating patches like '[PATCH 2.6.21-rc3]' or properly numbered series like '[-mm3 PATCH N/M]'. This patch provides the implementation and documentation. Signed-off-by: Robin H. Johnson Signed-off-by: Junio C Hamano --- Documentation/git-format-patch.txt | 17 ++++++++++++----- builtin-log.c | 10 ++++++++-- log-tree.c | 14 ++++++++++---- revision.h | 1 + 4 files changed, 31 insertions(+), 11 deletions(-) diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt index 111d7c60bf..a33d157b97 100644 --- a/Documentation/git-format-patch.txt +++ b/Documentation/git-format-patch.txt @@ -10,11 +10,12 @@ SYNOPSIS -------- [verse] 'git-format-patch' [-n | -k] [-o | --stdout] [--thread] - [--attach[=] | --inline[=]] - [-s | --signoff] [] [--start-number ] - [--in-reply-to=Message-Id] [--suffix=.] - [--ignore-if-in-upstream] - [..] + [--attach[=] | --inline[=]] + [-s | --signoff] [] [--start-number ] + [--in-reply-to=Message-Id] [--suffix=.] + [--ignore-if-in-upstream] + [--subject-prefix=Subject-Prefix] + [..] DESCRIPTION ----------- @@ -98,6 +99,12 @@ include::diff-options.txt[] patches being generated, and any patch that matches is ignored. +--subject-prefix=:: + Instead of the standard '[PATCH]' prefix in the subject + line, instead use '[]'. This + allows for useful naming of a patch series, and can be + combined with the --numbered option. + --suffix=.:: Instead of using `.patch` as the suffix for generated filenames, use specifed suffix. A common alternative is diff --git a/builtin-log.c b/builtin-log.c index 71df957eaa..4a4890aca0 100644 --- a/builtin-log.c +++ b/builtin-log.c @@ -417,6 +417,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) int numbered = 0; int start_number = -1; int keep_subject = 0; + int subject_prefix = 0; int ignore_if_in_upstream = 0; int thread = 0; const char *in_reply_to = NULL; @@ -434,6 +435,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) rev.ignore_merges = 1; rev.diffopt.msg_sep = ""; rev.diffopt.recursive = 1; + rev.subject_prefix = "PATCH"; rev.extra_headers = extra_headers; @@ -509,8 +511,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) if (i == argc) die("Need a Message-Id for --in-reply-to"); in_reply_to = argv[i]; - } - else if (!prefixcmp(argv[i], "--suffix=")) + } else if (!prefixcmp(argv[i], "--subject-prefix=")) { + subject_prefix = 1; + rev.subject_prefix = argv[i] + 17; + } else if (!prefixcmp(argv[i], "--suffix=")) fmt_patch_suffix = argv[i] + 9; else argv[j++] = argv[i]; @@ -521,6 +525,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) start_number = 1; if (numbered && keep_subject) die ("-n and -k are mutually exclusive."); + if (keep_subject && subject_prefix) + die ("--subject-prefix and -k are mutually exclusive."); argc = setup_revisions(argc, argv, &rev, "HEAD"); if (argc > 1) diff --git a/log-tree.c b/log-tree.c index 8797aa14c4..dad5513230 100644 --- a/log-tree.c +++ b/log-tree.c @@ -165,14 +165,20 @@ void show_log(struct rev_info *opt, const char *sep) if (opt->total > 0) { static char buffer[64]; snprintf(buffer, sizeof(buffer), - "Subject: [PATCH %0*d/%d] ", + "Subject: [%s %0*d/%d] ", + opt->subject_prefix, digits_in_number(opt->total), opt->nr, opt->total); subject = buffer; - } else if (opt->total == 0) - subject = "Subject: [PATCH] "; - else + } else if (opt->total == 0) { + static char buffer[256]; + snprintf(buffer, sizeof(buffer), + "Subject: [%s] ", + opt->subject_prefix); + subject = buffer; + } else { subject = "Subject: "; + } printf("From %s Mon Sep 17 00:00:00 2001\n", sha1); if (opt->message_id) diff --git a/revision.h b/revision.h index 55e6b531ce..5f3f628a9b 100644 --- a/revision.h +++ b/revision.h @@ -78,6 +78,7 @@ struct rev_info { const char *add_signoff; const char *extra_headers; const char *log_reencode; + const char *subject_prefix; int no_inline; /* Filter by commit log message */ From 171ddd91771f042e49db49ff068694b5ed6f845d Mon Sep 17 00:00:00 2001 From: "Robin H. Johnson" Date: Wed, 11 Apr 2007 16:58:08 -0700 Subject: [PATCH 53/90] Add testcase for format-patch --subject-prefix (take 3) Add testcase for format-patch --subject-prefix support. Signed-off-by: Robin H. Johnson Signed-off-by: Junio C Hamano --- t/t4013-diff-various.sh | 1 + ..._--subject-prefix=TESTCASE_initial..master | 164 ++++++++++++++++++ 2 files changed, 165 insertions(+) create mode 100644 t/t4013/diff.format-patch_--inline_--stdout_--subject-prefix=TESTCASE_initial..master diff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh index 488e075c16..8f4c29a6b5 100755 --- a/t/t4013-diff-various.sh +++ b/t/t4013-diff-various.sh @@ -241,6 +241,7 @@ format-patch --attach --stdout initial..master format-patch --inline --stdout initial..side format-patch --inline --stdout initial..master^ format-patch --inline --stdout initial..master +format-patch --inline --stdout --subject-prefix=TESTCASE initial..master diff --abbrev initial..side diff -r initial..side diff --git a/t/t4013/diff.format-patch_--inline_--stdout_--subject-prefix=TESTCASE_initial..master b/t/t4013/diff.format-patch_--inline_--stdout_--subject-prefix=TESTCASE_initial..master new file mode 100644 index 0000000000..a8093be7ca --- /dev/null +++ b/t/t4013/diff.format-patch_--inline_--stdout_--subject-prefix=TESTCASE_initial..master @@ -0,0 +1,164 @@ +$ git format-patch --inline --stdout --subject-prefix=TESTCASE initial..master +From 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 Mon Sep 17 00:00:00 2001 +From: A U Thor +Date: Mon, 26 Jun 2006 00:01:00 +0000 +Subject: [TESTCASE] Second +MIME-Version: 1.0 +Content-Type: multipart/mixed; boundary="------------g-i-t--v-e-r-s-i-o-n" + +This is a multi-part message in MIME format. +--------------g-i-t--v-e-r-s-i-o-n +Content-Type: text/plain; charset=UTF-8; format=fixed +Content-Transfer-Encoding: 8bit + + +This is the second commit. +--- + dir/sub | 2 ++ + file0 | 3 +++ + file2 | 3 --- + 3 files changed, 5 insertions(+), 3 deletions(-) + delete mode 100644 file2 +--------------g-i-t--v-e-r-s-i-o-n +Content-Type: text/x-patch; name="1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44.diff" +Content-Transfer-Encoding: 8bit +Content-Disposition: inline; filename="1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44.diff" + +diff --git a/dir/sub b/dir/sub +index 35d242b..8422d40 100644 +--- a/dir/sub ++++ b/dir/sub +@@ -1,2 +1,4 @@ + A + B ++C ++D +diff --git a/file0 b/file0 +index 01e79c3..b414108 100644 +--- a/file0 ++++ b/file0 +@@ -1,3 +1,6 @@ + 1 + 2 + 3 ++4 ++5 ++6 +diff --git a/file2 b/file2 +deleted file mode 100644 +index 01e79c3..0000000 +--- a/file2 ++++ /dev/null +@@ -1,3 +0,0 @@ +-1 +-2 +-3 + +--------------g-i-t--v-e-r-s-i-o-n-- + + + +From 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 Mon Sep 17 00:00:00 2001 +From: A U Thor +Date: Mon, 26 Jun 2006 00:02:00 +0000 +Subject: [TESTCASE] Third +MIME-Version: 1.0 +Content-Type: multipart/mixed; boundary="------------g-i-t--v-e-r-s-i-o-n" + +This is a multi-part message in MIME format. +--------------g-i-t--v-e-r-s-i-o-n +Content-Type: text/plain; charset=UTF-8; format=fixed +Content-Transfer-Encoding: 8bit + +--- + dir/sub | 2 ++ + file1 | 3 +++ + 2 files changed, 5 insertions(+), 0 deletions(-) + create mode 100644 file1 +--------------g-i-t--v-e-r-s-i-o-n +Content-Type: text/x-patch; name="9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0.diff" +Content-Transfer-Encoding: 8bit +Content-Disposition: inline; filename="9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0.diff" + +diff --git a/dir/sub b/dir/sub +index 8422d40..cead32e 100644 +--- a/dir/sub ++++ b/dir/sub +@@ -2,3 +2,5 @@ A + B + C + D ++E ++F +diff --git a/file1 b/file1 +new file mode 100644 +index 0000000..b1e6722 +--- /dev/null ++++ b/file1 +@@ -0,0 +1,3 @@ ++A ++B ++C + +--------------g-i-t--v-e-r-s-i-o-n-- + + + +From c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a Mon Sep 17 00:00:00 2001 +From: A U Thor +Date: Mon, 26 Jun 2006 00:03:00 +0000 +Subject: [TESTCASE] Side +MIME-Version: 1.0 +Content-Type: multipart/mixed; boundary="------------g-i-t--v-e-r-s-i-o-n" + +This is a multi-part message in MIME format. +--------------g-i-t--v-e-r-s-i-o-n +Content-Type: text/plain; charset=UTF-8; format=fixed +Content-Transfer-Encoding: 8bit + +--- + dir/sub | 2 ++ + file0 | 3 +++ + file3 | 4 ++++ + 3 files changed, 9 insertions(+), 0 deletions(-) + create mode 100644 file3 +--------------g-i-t--v-e-r-s-i-o-n +Content-Type: text/x-patch; name="c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a.diff" +Content-Transfer-Encoding: 8bit +Content-Disposition: inline; filename="c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a.diff" + +diff --git a/dir/sub b/dir/sub +index 35d242b..7289e35 100644 +--- a/dir/sub ++++ b/dir/sub +@@ -1,2 +1,4 @@ + A + B ++1 ++2 +diff --git a/file0 b/file0 +index 01e79c3..f4615da 100644 +--- a/file0 ++++ b/file0 +@@ -1,3 +1,6 @@ + 1 + 2 + 3 ++A ++B ++C +diff --git a/file3 b/file3 +new file mode 100644 +index 0000000..7289e35 +--- /dev/null ++++ b/file3 +@@ -0,0 +1,4 @@ ++A ++B ++1 ++2 + +--------------g-i-t--v-e-r-s-i-o-n-- + + +$ From 6aead43db34313e6cdbc72e2f7a70f6b82c78cf2 Mon Sep 17 00:00:00 2001 From: Jim Meyering Date: Tue, 10 Apr 2007 01:01:44 +0200 Subject: [PATCH 54/90] sscanf/strtoul: parse integers robustly * builtin-grep.c (strtoul_ui): Move function definition from here, to... * git-compat-util.h (strtoul_ui): ...here, with an added "base" parameter. * builtin-grep.c (cmd_grep): Update use of strtoul_ui to include base, "10". * builtin-update-index.c (read_index_info): Diagnose an invalid mode integer that is out of range or merely larger than INT_MAX. (cmd_update_index): Use strtoul_ui, not sscanf. * convert-objects.c (write_subdirectory): Likewise. Signed-off-by: Jim Meyering Signed-off-by: Junio C Hamano --- builtin-grep.c | 15 +-------------- builtin-update-index.c | 10 +++++++--- convert-objects.c | 2 +- git-compat-util.h | 13 +++++++++++++ 4 files changed, 22 insertions(+), 18 deletions(-) diff --git a/builtin-grep.c b/builtin-grep.c index 981f3d4d8e..e13cb31f2b 100644 --- a/builtin-grep.c +++ b/builtin-grep.c @@ -434,19 +434,6 @@ static const char emsg_missing_context_len[] = static const char emsg_missing_argument[] = "option requires an argument -%s"; -static int strtoul_ui(char const *s, unsigned int *result) -{ - unsigned long ul; - char *p; - - errno = 0; - ul = strtoul(s, &p, 10); - if (errno || *p || p == s || (unsigned int) ul != ul) - return -1; - *result = ul; - return 0; -} - int cmd_grep(int argc, const char **argv, const char *prefix) { int hit = 0; @@ -569,7 +556,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix) scan = arg + 1; break; } - if (strtoul_ui(scan, &num)) + if (strtoul_ui(scan, 10, &num)) die(emsg_invalid_context_len, scan); switch (arg[1]) { case 'A': diff --git a/builtin-update-index.c b/builtin-update-index.c index 47d42ed645..b3d4acee6d 100644 --- a/builtin-update-index.c +++ b/builtin-update-index.c @@ -227,6 +227,7 @@ static void read_index_info(int line_termination) char *path_name; unsigned char sha1[20]; unsigned int mode; + unsigned long ul; int stage; /* This reads lines formatted in one of three formats: @@ -249,9 +250,12 @@ static void read_index_info(int line_termination) if (buf.eof) break; - mode = strtoul(buf.buf, &ptr, 8); - if (ptr == buf.buf || *ptr != ' ') + errno = 0; + ul = strtoul(buf.buf, &ptr, 8); + if (ptr == buf.buf || *ptr != ' ' + || errno || (unsigned int) ul != ul) goto bad_line; + mode = ul; tab = strchr(ptr, '\t'); if (!tab || tab - ptr < 41) @@ -547,7 +551,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix) if (i+3 >= argc) die("git-update-index: --cacheinfo "); - if ((sscanf(argv[i+1], "%o", &mode) != 1) || + if ((strtoul_ui(argv[i+1], 8, &mode) != 1) || get_sha1_hex(argv[i+2], sha1) || add_cacheinfo(mode, sha1, argv[i+3], 0)) die("git-update-index: --cacheinfo" diff --git a/convert-objects.c b/convert-objects.c index 4809f9199f..cf03bcfe5a 100644 --- a/convert-objects.c +++ b/convert-objects.c @@ -88,7 +88,7 @@ static int write_subdirectory(void *buffer, unsigned long size, const char *base unsigned int mode; char *slash, *origpath; - if (!path || sscanf(buffer, "%o", &mode) != 1) + if (!path || strtoul_ui(buffer, 8, &mode) != 1) die("bad tree conversion"); mode = convert_mode(mode); path++; diff --git a/git-compat-util.h b/git-compat-util.h index 139fc19108..5f6a281b78 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -301,4 +301,17 @@ static inline int prefixcmp(const char *str, const char *prefix) return strncmp(str, prefix, strlen(prefix)); } +static inline int strtoul_ui(char const *s, int base, unsigned int *result) +{ + unsigned long ul; + char *p; + + errno = 0; + ul = strtoul(s, &p, base); + if (errno || *p || p == s || (unsigned int) ul != ul) + return -1; + *result = ul; + return 0; +} + #endif From 199c45bf2b168cb8b2231e45f35e5fd588c2fc19 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 9 Apr 2007 02:34:05 -0700 Subject: [PATCH 55/90] Add %m to '--pretty=format:' When used with '--boundary A...B', this shows the -/ marker you would get with --left-right option to 'git-log' family. When symmetric diff is not used, everybody is shown to be on the "right" branch. Signed-off-by: Junio C Hamano --- Documentation/pretty-formats.txt | 1 + commit.c | 16 +++++++++++++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt index 2fe6c31967..d7ffc21ddf 100644 --- a/Documentation/pretty-formats.txt +++ b/Documentation/pretty-formats.txt @@ -117,6 +117,7 @@ The placeholders are: - '%Cgreen': switch color to green - '%Cblue': switch color to blue - '%Creset': reset color +- '%m': left, right or boundary mark - '%n': newline diff --git a/commit.c b/commit.c index 754d1b8a0b..952095faa7 100644 --- a/commit.c +++ b/commit.c @@ -4,6 +4,8 @@ #include "pkt-line.h" #include "utf8.h" #include "interpolate.h" +#include "diff.h" +#include "revision.h" int save_commit_buffer = 1; @@ -808,7 +810,8 @@ static long format_commit_message(const struct commit *commit, { "%Cgreen" }, /* green */ { "%Cblue" }, /* blue */ { "%Creset" }, /* reset color */ - { "%n" } /* newline */ + { "%n" }, /* newline */ + { "%m" }, /* left/right/bottom */ }; enum interp_index { IHASH = 0, IHASH_ABBREV, @@ -824,14 +827,15 @@ static long format_commit_message(const struct commit *commit, ISUBJECT, IBODY, IRED, IGREEN, IBLUE, IRESET_COLOR, - INEWLINE + INEWLINE, + ILEFT_RIGHT, }; struct commit_list *p; char parents[1024]; int i; enum { HEADER, SUBJECT, BODY } state; - if (INEWLINE + 1 != ARRAY_SIZE(table)) + if (ILEFT_RIGHT + 1 != ARRAY_SIZE(table)) die("invalid interp table!"); /* these are independent of the commit */ @@ -852,6 +856,12 @@ static long format_commit_message(const struct commit *commit, interp_set_entry(table, ITREE_ABBREV, find_unique_abbrev(commit->tree->object.sha1, DEFAULT_ABBREV)); + interp_set_entry(table, ILEFT_RIGHT, + (commit->object.flags & BOUNDARY) + ? "-" + : (commit->object.flags & SYMMETRIC_LEFT) + ? "<" + : ">"); parents[1] = 0; for (i = 0, p = commit->parents; From 5d23e133d23bc9e26d6f23a4d136901e18e6ffba Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 9 Apr 2007 17:01:27 -0700 Subject: [PATCH 56/90] Refactor patch-id filtering out of git-cherry and git-format-patch. This implements the patch-id computation and recording library, patch-ids.c, and rewrites the get_patch_ids() function used in cherry and format-patch to use it, so that they do not pollute the object namespace. Earlier code threw non-objects into the in-core object database, and hoped for not getting bitten by SHA-1 collisions. While it may be practically Ok, it still was an ugly hack. Signed-off-by: Junio C Hamano --- Makefile | 3 +- builtin-log.c | 44 ++++-------- patch-ids.c | 192 ++++++++++++++++++++++++++++++++++++++++++++++++++ patch-ids.h | 21 ++++++ 4 files changed, 228 insertions(+), 32 deletions(-) create mode 100644 patch-ids.c create mode 100644 patch-ids.h diff --git a/Makefile b/Makefile index a77d31de98..f956c3d9ba 100644 --- a/Makefile +++ b/Makefile @@ -283,7 +283,7 @@ LIB_H = \ diff.h object.h pack.h pkt-line.h quote.h refs.h list-objects.h sideband.h \ run-command.h strbuf.h tag.h tree.h git-compat-util.h revision.h \ tree-walk.h log-tree.h dir.h path-list.h unpack-trees.h builtin.h \ - utf8.h reflog-walk.h + utf8.h reflog-walk.h patch-ids.h DIFF_OBJS = \ diff.o diff-lib.o diffcore-break.o diffcore-order.o \ @@ -295,6 +295,7 @@ LIB_OBJS = \ date.o diff-delta.o entry.o exec_cmd.o ident.o \ interpolate.o \ lockfile.o \ + patch-ids.o \ object.o pack-check.o patch-delta.o path.o pkt-line.o sideband.o \ reachable.o reflog-walk.o \ quote.o read-cache.o refs.o run-command.o dir.o object-refs.o \ diff --git a/builtin-log.c b/builtin-log.c index 4a4890aca0..eea96901b9 100644 --- a/builtin-log.c +++ b/builtin-log.c @@ -12,6 +12,7 @@ #include "builtin.h" #include "tag.h" #include "reflog-walk.h" +#include "patch-ids.h" static int default_show_root = 1; @@ -333,25 +334,12 @@ static int reopen_stdout(struct commit *commit, int nr, int keep_subject) } -static int get_patch_id(struct commit *commit, struct diff_options *options, - unsigned char *sha1) -{ - if (commit->parents) - diff_tree_sha1(commit->parents->item->object.sha1, - commit->object.sha1, "", options); - else - diff_root_tree_sha1(commit->object.sha1, "", options); - diffcore_std(options); - return diff_flush_patch_id(options, sha1); -} - -static void get_patch_ids(struct rev_info *rev, struct diff_options *options, const char *prefix) +static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids, const char *prefix) { struct rev_info check_rev; struct commit *commit; struct object *o1, *o2; unsigned flags1, flags2; - unsigned char sha1[20]; if (rev->pending.nr != 2) die("Need exactly one range."); @@ -364,10 +352,7 @@ static void get_patch_ids(struct rev_info *rev, struct diff_options *options, co if ((flags1 & UNINTERESTING) == (flags2 & UNINTERESTING)) die("Not a range."); - diff_setup(options); - options->recursive = 1; - if (diff_setup_done(options) < 0) - die("diff_setup_done failed"); + init_patch_ids(ids); /* given a range a..b get all patch ids for b..a */ init_revisions(&check_rev, prefix); @@ -382,8 +367,7 @@ static void get_patch_ids(struct rev_info *rev, struct diff_options *options, co if (commit->parents && commit->parents->next) continue; - if (!get_patch_id(commit, options, sha1)) - created_object(sha1, xcalloc(1, sizeof(struct object))); + add_commit_patch_id(commit, ids); } /* reset for next revision walk */ @@ -421,7 +405,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) int ignore_if_in_upstream = 0; int thread = 0; const char *in_reply_to = NULL; - struct diff_options patch_id_opts; + struct patch_ids ids; char *add_signoff = NULL; char message_id[1024]; char ref_message_id[1024]; @@ -560,22 +544,19 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) } if (ignore_if_in_upstream) - get_patch_ids(&rev, &patch_id_opts, prefix); + get_patch_ids(&rev, &ids, prefix); if (!use_stdout) realstdout = fdopen(dup(1), "w"); prepare_revision_walk(&rev); while ((commit = get_revision(&rev)) != NULL) { - unsigned char sha1[20]; - /* ignore merges */ if (commit->parents && commit->parents->next) continue; if (ignore_if_in_upstream && - !get_patch_id(commit, &patch_id_opts, sha1) && - lookup_object(sha1)) + has_commit_patch_id(commit, &ids)) continue; nr++; @@ -630,6 +611,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) fclose(stdout); } free(list); + if (ignore_if_in_upstream) + free_patch_ids(&ids); return 0; } @@ -652,7 +635,7 @@ static const char cherry_usage[] = int cmd_cherry(int argc, const char **argv, const char *prefix) { struct rev_info revs; - struct diff_options patch_id_opts; + struct patch_ids ids; struct commit *commit; struct commit_list *list = NULL; const char *upstream; @@ -698,7 +681,7 @@ int cmd_cherry(int argc, const char **argv, const char *prefix) return 0; } - get_patch_ids(&revs, &patch_id_opts, prefix); + get_patch_ids(&revs, &ids, prefix); if (limit && add_pending_commit(limit, &revs, UNINTERESTING)) die("Unknown commit %s", limit); @@ -714,12 +697,10 @@ int cmd_cherry(int argc, const char **argv, const char *prefix) } while (list) { - unsigned char sha1[20]; char sign = '+'; commit = list->item; - if (!get_patch_id(commit, &patch_id_opts, sha1) && - lookup_object(sha1)) + if (has_commit_patch_id(commit, &ids)) sign = '-'; if (verbose) { @@ -737,5 +718,6 @@ int cmd_cherry(int argc, const char **argv, const char *prefix) list = list->next; } + free_patch_ids(&ids); return 0; } diff --git a/patch-ids.c b/patch-ids.c new file mode 100644 index 0000000000..a288fac992 --- /dev/null +++ b/patch-ids.c @@ -0,0 +1,192 @@ +#include "cache.h" +#include "diff.h" +#include "commit.h" +#include "patch-ids.h" + +static int commit_patch_id(struct commit *commit, struct diff_options *options, + unsigned char *sha1) +{ + if (commit->parents) + diff_tree_sha1(commit->parents->item->object.sha1, + commit->object.sha1, "", options); + else + diff_root_tree_sha1(commit->object.sha1, "", options); + diffcore_std(options); + return diff_flush_patch_id(options, sha1); +} + +static uint32_t take2(const unsigned char *id) +{ + return ((id[0] << 8) | id[1]); +} + +/* + * Conventional binary search loop looks like this: + * + * do { + * int mi = (lo + hi) / 2; + * int cmp = "entry pointed at by mi" minus "target"; + * if (!cmp) + * return (mi is the wanted one) + * if (cmp > 0) + * hi = mi; "mi is larger than target" + * else + * lo = mi+1; "mi is smaller than target" + * } while (lo < hi); + * + * The invariants are: + * + * - When entering the loop, lo points at a slot that is never + * above the target (it could be at the target), hi points at a + * slot that is guaranteed to be above the target (it can never + * be at the target). + * + * - We find a point 'mi' between lo and hi (mi could be the same + * as lo, but never can be the same as hi), and check if it hits + * the target. There are three cases: + * + * - if it is a hit, we are happy. + * + * - if it is strictly higher than the target, we update hi with + * it. + * + * - if it is strictly lower than the target, we update lo to be + * one slot after it, because we allow lo to be at the target. + * + * When choosing 'mi', we do not have to take the "middle" but + * anywhere in between lo and hi, as long as lo <= mi < hi is + * satisfied. When we somehow know that the distance between the + * target and lo is much shorter than the target and hi, we could + * pick mi that is much closer to lo than the midway. + */ +static int patch_pos(struct patch_id **table, int nr, const unsigned char *id) +{ + int hi = nr; + int lo = 0; + int mi = 0; + + if (!nr) + return -1; + + if (nr != 1) { + unsigned lov, hiv, miv, ofs; + + for (ofs = 0; ofs < 18; ofs += 2) { + lov = take2(table[0]->patch_id + ofs); + hiv = take2(table[nr-1]->patch_id + ofs); + miv = take2(id + ofs); + if (miv < lov) + return -1; + if (hiv < miv) + return -1 - nr; + if (lov != hiv) { + /* + * At this point miv could be equal + * to hiv (but id could still be higher); + * the invariant of (mi < hi) should be + * kept. + */ + mi = (nr-1) * (miv - lov) / (hiv - lov); + if (lo <= mi && mi < hi) + break; + die("oops"); + } + } + if (18 <= ofs) + die("cannot happen -- lo and hi are identical"); + } + + do { + int cmp; + cmp = hashcmp(table[mi]->patch_id, id); + if (!cmp) + return mi; + if (cmp > 0) + hi = mi; + else + lo = mi + 1; + mi = (hi + lo) / 2; + } while (lo < hi); + return -lo-1; +} + +#define BUCKET_SIZE 190 /* 190 * 21 = 3990, with slop close enough to 4K */ +struct patch_id_bucket { + struct patch_id_bucket *next; + int nr; + struct patch_id bucket[BUCKET_SIZE]; +}; + +int init_patch_ids(struct patch_ids *ids) +{ + memset(ids, 0, sizeof(*ids)); + diff_setup(&ids->diffopts); + ids->diffopts.recursive = 1; + if (diff_setup_done(&ids->diffopts) < 0) + return error("diff_setup_done failed"); + return 0; +} + +int free_patch_ids(struct patch_ids *ids) +{ + struct patch_id_bucket *next, *patches; + + free(ids->table); + for (patches = ids->patches; patches; patches = next) { + next = patches->next; + free(patches); + } + return 0; +} + +static struct patch_id *add_commit(struct commit *commit, + struct patch_ids *ids, + int no_add) +{ + struct patch_id_bucket *bucket; + struct patch_id *ent; + unsigned char sha1[20]; + int pos; + + if (commit_patch_id(commit, &ids->diffopts, sha1)) + return NULL; + pos = patch_pos(ids->table, ids->nr, sha1); + if (0 <= pos) + return ids->table[pos]; + if (no_add) + return NULL; + + pos = -1 - pos; + + bucket = ids->patches; + if (!bucket || (BUCKET_SIZE <= bucket->nr)) { + bucket = xcalloc(1, sizeof(*bucket)); + bucket->next = ids->patches; + ids->patches = bucket; + } + ent = &bucket->bucket[bucket->nr++]; + hashcpy(ent->patch_id, sha1); + + if (ids->alloc <= ids->nr) { + ids->alloc = alloc_nr(ids->nr); + ids->table = xrealloc(ids->table, sizeof(ent) * ids->alloc); + } + if (pos < ids->nr) + memmove(ids->table + pos + 1, ids->table + pos, + sizeof(ent) * (ids->nr - pos)); + ids->nr++; + ids->table[pos] = ent; + return ids->table[pos]; +} + +struct patch_id *has_commit_patch_id(struct commit *commit, + struct patch_ids *ids) +{ + return add_commit(commit, ids, 1); +} + +struct patch_id *add_commit_patch_id(struct commit *commit, + struct patch_ids *ids) +{ + return add_commit(commit, ids, 0); +} diff --git a/patch-ids.h b/patch-ids.h new file mode 100644 index 0000000000..c8c7ca110a --- /dev/null +++ b/patch-ids.h @@ -0,0 +1,21 @@ +#ifndef PATCH_IDS_H +#define PATCH_IDS_H + +struct patch_id { + unsigned char patch_id[20]; + char seen; +}; + +struct patch_ids { + struct diff_options diffopts; + int nr, alloc; + struct patch_id **table; + struct patch_id_bucket *patches; +}; + +int init_patch_ids(struct patch_ids *); +int free_patch_ids(struct patch_ids *); +struct patch_id *add_commit_patch_id(struct commit *, struct patch_ids *); +struct patch_id *has_commit_patch_id(struct commit *, struct patch_ids *); + +#endif /* PATCH_IDS_H */ From d7a17cad9798693dc31043aa4efcce0b207483b9 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 9 Apr 2007 03:40:38 -0700 Subject: [PATCH 57/90] git-log --cherry-pick A...B This is meant to be a saner replacement for "git-cherry". When used with "A...B", this filters out commits whose patch text has the same patch-id as a commit on the other side. It would probably most useful to use with --left-right. Signed-off-by: Junio C Hamano --- revision.c | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ revision.h | 1 + 2 files changed, 89 insertions(+) diff --git a/revision.c b/revision.c index 486393cb08..e9de8650d3 100644 --- a/revision.c +++ b/revision.c @@ -8,6 +8,7 @@ #include "revision.h" #include "grep.h" #include "reflog-walk.h" +#include "patch-ids.h" static char *path_name(struct name_path *path, const char *name) { @@ -422,6 +423,86 @@ static void add_parents_to_list(struct rev_info *revs, struct commit *commit, st } } +static void cherry_pick_list(struct commit_list *list) +{ + struct commit_list *p; + int left_count = 0, right_count = 0; + int left_first; + struct patch_ids ids; + + /* First count the commits on the left and on the right */ + for (p = list; p; p = p->next) { + struct commit *commit = p->item; + unsigned flags = commit->object.flags; + if (flags & BOUNDARY) + ; + else if (flags & SYMMETRIC_LEFT) + left_count++; + else + right_count++; + } + + left_first = left_count < right_count; + init_patch_ids(&ids); + + /* Compute patch-ids for one side */ + for (p = list; p; p = p->next) { + struct commit *commit = p->item; + unsigned flags = commit->object.flags; + + if (flags & BOUNDARY) + continue; + /* + * If we have fewer left, left_first is set and we omit + * commits on the right branch in this loop. If we have + * fewer right, we skip the left ones. + */ + if (left_first != !!(flags & SYMMETRIC_LEFT)) + continue; + commit->util = add_commit_patch_id(commit, &ids); + } + + /* Check the other side */ + for (p = list; p; p = p->next) { + struct commit *commit = p->item; + struct patch_id *id; + unsigned flags = commit->object.flags; + + if (flags & BOUNDARY) + continue; + /* + * If we have fewer left, left_first is set and we omit + * commits on the left branch in this loop. + */ + if (left_first == !!(flags & SYMMETRIC_LEFT)) + continue; + + /* + * Have we seen the same patch id? + */ + id = has_commit_patch_id(commit, &ids); + if (!id) + continue; + id->seen = 1; + commit->object.flags |= SHOWN; + } + + /* Now check the original side for seen ones */ + for (p = list; p; p = p->next) { + struct commit *commit = p->item; + struct patch_id *ent; + + ent = commit->util; + if (!ent) + continue; + if (ent->seen) + commit->object.flags |= SHOWN; + commit->util = NULL; + } + + free_patch_ids(&ids); +} + static void limit_list(struct rev_info *revs) { struct commit_list *list = revs->commits; @@ -449,6 +530,9 @@ static void limit_list(struct rev_info *revs) continue; p = &commit_list_insert(commit, p)->next; } + if (revs->cherry_pick) + cherry_pick_list(newlist); + revs->commits = newlist; } @@ -913,6 +997,10 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch revs->left_right = 1; continue; } + if (!strcmp(arg, "--cherry-pick")) { + revs->cherry_pick = 1; + continue; + } if (!strcmp(arg, "--objects")) { revs->tag_objects = 1; revs->tree_objects = 1; diff --git a/revision.h b/revision.h index 5f3f628a9b..8a02618428 100644 --- a/revision.h +++ b/revision.h @@ -47,6 +47,7 @@ struct rev_info { left_right:1, parents:1, reverse:1, + cherry_pick:1, first_parent_only:1; /* Diff flags */ From 55a643ed1b9b9af4d3bd9dc934dfccd142d1c632 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 10 Apr 2007 15:28:32 -0700 Subject: [PATCH 58/90] Documentation: --cherry-pick Signed-off-by: Junio C Hamano --- Documentation/git-rev-list.txt | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/Documentation/git-rev-list.txt b/Documentation/git-rev-list.txt index 12b71ed0bb..77e068b15f 100644 --- a/Documentation/git-rev-list.txt +++ b/Documentation/git-rev-list.txt @@ -22,6 +22,7 @@ SYNOPSIS [ \--topo-order ] [ \--parents ] [ \--left-right ] + [ \--cherry-pick ] [ \--encoding[=] ] [ \--(author|committer|grep)= ] [ [\--objects | \--objects-edge] [ \--unpacked ] ] @@ -224,6 +225,20 @@ limiting may be applied. In addition to the '' listed on the command line, read them from the standard input. +--cherry-pick:: + + Omit any commit that introduces the same change as + another commit on the "other side" when the set of + commits are limited with symmetric difference. ++ +For example, if you have two branches, `A` and `B`, a usual way +to list all commits on only one side of them is with +`--left-right`, like the example above in the description of +that option. It however shows the commits that were cherry-picked +from the other branch (for example, "3rd on b" may be cherry-picked +from branch A). With this option, such pairs of commits are +excluded from the output. + -g, --walk-reflogs:: Instead of walking the commit ancestry chain, walk From 9aef12673e966fcbefea7716239e159c03b8d94e Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Thu, 12 Apr 2007 01:21:18 -0400 Subject: [PATCH 59/90] Don't yap about merge-subtree during make By default we are pretty quiet about the actual commands that we are running. So we should continue to be quiet about the new merge-subtree hardlink to merge-recursive. Technically this is not a builtin, but it is close because subtree is actually builtin to a non-builtin. So lets just make things easy and call it a builtin. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index a77d31de98..d6c5d596b8 100644 --- a/Makefile +++ b/Makefile @@ -728,7 +728,7 @@ git$X: git.c common-cmds.h $(BUILTIN_OBJS) $(GITLIBS) GIT-CFLAGS help.o: common-cmds.h git-merge-subtree$X: git-merge-recursive$X - rm -f $@ && ln git-merge-recursive$X $@ + $(QUIET_BUILT_IN)rm -f $@ && ln git-merge-recursive$X $@ $(BUILT_INS): git$X $(QUIET_BUILT_IN)rm -f $@ && ln git$X $@ From 2bfe3cec92be4f5e3bfc0e71ed560df4a726c07b Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 12 Apr 2007 03:04:05 -0700 Subject: [PATCH 60/90] Fix git {log,show,...} --pretty=email An earlier --subject-prefix patch forgot that format-patch is not the only codepath that adds the "[PATCH]" prefix, and broke everybody else in the log family. Signed-off-by: Junio C Hamano --- builtin-log.c | 1 - revision.c | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin-log.c b/builtin-log.c index 4a4890aca0..ffc269a122 100644 --- a/builtin-log.c +++ b/builtin-log.c @@ -435,7 +435,6 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) rev.ignore_merges = 1; rev.diffopt.msg_sep = ""; rev.diffopt.recursive = 1; - rev.subject_prefix = "PATCH"; rev.extra_headers = extra_headers; diff --git a/revision.c b/revision.c index 486393cb08..37f1eab9e5 100644 --- a/revision.c +++ b/revision.c @@ -567,6 +567,7 @@ void init_revisions(struct rev_info *revs, const char *prefix) revs->min_age = -1; revs->skip_count = -1; revs->max_count = -1; + revs->subject_prefix = "PATCH"; revs->prune_fn = NULL; revs->prune_data = NULL; From e4b023332c687d824cff41855b41f604ee72e0d2 Mon Sep 17 00:00:00 2001 From: Matthias Lederhofer Date: Thu, 12 Apr 2007 20:52:03 +0200 Subject: [PATCH 61/90] handle_options in git wrapper miscounts the options it handled. handle_options did not count the number of used arguments correctly. When --git-dir was used the extra argument was not added to the number of handled arguments. Signed-off-by: Matthias Lederhofer Signed-off-by: Junio C Hamano --- git.c | 1 + 1 file changed, 1 insertion(+) diff --git a/git.c b/git.c index 33dd4d39d9..7def319e60 100644 --- a/git.c +++ b/git.c @@ -66,6 +66,7 @@ static int handle_options(const char*** argv, int* argc) setenv(GIT_DIR_ENVIRONMENT, (*argv)[1], 1); (*argv)++; (*argc)--; + handled++; } else if (!prefixcmp(cmd, "--git-dir=")) { setenv(GIT_DIR_ENVIRONMENT, cmd + 10, 1); } else if (!strcmp(cmd, "--bare")) { From dc61b10d987d32522803044eccaa801ffbca4bfe Mon Sep 17 00:00:00 2001 From: Alex Riesen Date: Sat, 14 Apr 2007 00:19:05 +0200 Subject: [PATCH 62/90] Use rev-list --reverse in git-rebase.sh ...and drop the last perl dependency in the script. Signed-off-by: Alex Riesen Signed-off-by: Junio C Hamano --- git-rebase.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/git-rebase.sh b/git-rebase.sh index 1d96f32685..2dc2c4fe9b 100755 --- a/git-rebase.sh +++ b/git-rebase.sh @@ -339,8 +339,7 @@ prev_head=`git-rev-parse HEAD^0` echo "$prev_head" > "$dotest/prev_head" msgnum=0 -for cmt in `git-rev-list --no-merges "$upstream"..ORIG_HEAD \ - | @@PERL@@ -e 'print reverse <>'` +for cmt in `git-rev-list --reverse --no-merges "$upstream"..ORIG_HEAD` do msgnum=$(($msgnum + 1)) echo "$cmt" > "$dotest/cmt.$msgnum" From 1ad029b6a1e2fb0254667a2bea8d1ee180cc6ac7 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 13 Apr 2007 03:23:20 -0700 Subject: [PATCH 63/90] Do not default to --no-index when given two directories. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit git-diff -- a/ b/ always defaulted to --no-index, primarily because the function is_in_index() was implemented quite incorrectly. Noticed by Patrick Maaß and Simon Schubert independently, initial patch was provided by Patrick but I fixed it differently. Signed-off-by: Junio C Hamano --- diff-lib.c | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/diff-lib.c b/diff-lib.c index 5c5b05bfe3..7531e20c78 100644 --- a/diff-lib.c +++ b/diff-lib.c @@ -142,18 +142,34 @@ static int queue_diff(struct diff_options *o, } } +/* + * Does the path name a blob in the working tree, or a directory + * in the working tree? + */ static int is_in_index(const char *path) { - int len = strlen(path); - int pos = cache_name_pos(path, len); - char c; + int len, pos; + struct cache_entry *ce; - if (pos < 0) - return 0; - if (strncmp(active_cache[pos]->name, path, len)) - return 0; - c = active_cache[pos]->name[len]; - return c == '\0' || c == '/'; + len = strlen(path); + while (path[len-1] == '/') + len--; + if (!len) + return 1; /* "." */ + pos = cache_name_pos(path, len); + if (0 <= pos) + return 1; + pos = -1 - pos; + while (pos < active_nr) { + ce = active_cache[pos++]; + if (ce_namelen(ce) <= len || + strncmp(ce->name, path, len) || + (ce->name[len] > '/')) + break; /* path cannot be a prefix */ + if (ce->name[len] == '/') + return 1; + } + return 0; } static int handle_diff_files_args(struct rev_info *revs, From eabb0bfd09031d334152a268c47feb2bc3796a3e Mon Sep 17 00:00:00 2001 From: Frank Lichtenheld Date: Fri, 13 Apr 2007 18:02:30 +0200 Subject: [PATCH 64/90] config.txt: Document gitcvs.allbinary Signed-off-by: Frank Lichtenheld Signed-off-by: Junio C Hamano --- Documentation/config.txt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Documentation/config.txt b/Documentation/config.txt index cf1e040381..061c585629 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -408,6 +408,13 @@ gitcvs.logfile:: Path to a log file where the cvs pserver interface well... logs various stuff. See gitlink:git-cvsserver[1]. +gitcvs.allbinary:: + If true, all files are sent to the client in mode '-kb'. This + causes the client to treat all files as binary files which suppresses + any newline munging it otherwise might do. A work-around for the + fact that there is no way yet to set single files to mode '-kb'. + See gitlink:git-cvsserver[1]. + http.sslVerify:: Whether to verify the SSL certificate when fetching or pushing over HTTPS. Can be overridden by the 'GIT_SSL_NO_VERIFY' environment From 5cb71f82de7fc370dfcd7aad505c36d89e8fec5d Mon Sep 17 00:00:00 2001 From: Frank Lichtenheld Date: Fri, 13 Apr 2007 18:02:31 +0200 Subject: [PATCH 65/90] config.txt: Document core.autocrlf Text shamelessly stolen from the 1.5.1 release notes. Signed-off-by: Frank Lichtenheld Signed-off-by: Junio C Hamano --- Documentation/config.txt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Documentation/config.txt b/Documentation/config.txt index 061c585629..38831bc906 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -117,6 +117,16 @@ core.fileMode:: the working copy are ignored; useful on broken filesystems like FAT. See gitlink:git-update-index[1]. True by default. +core.autocrlf:: + If true, makes git convert `CRLF` at the end of lines in text files to + `LF` when reading from the filesystem, and convert in reverse when + writing to the filesystem. The variable can be set to + 'input', in which case the conversion happens only while + reading from the filesystem but files are written out with + `LF` at the end of lines. Currently, which paths to consider + "text" (i.e. be subjected to the autocrlf mechanism) is + decided purely based on the contents. + core.symlinks:: If false, symbolic links are checked out as small plain files that contain the link text. gitlink:git-update-index[1] and From 5007af8c7ed04ae5074ee84491526d416d460f97 Mon Sep 17 00:00:00 2001 From: Frank Lichtenheld Date: Fri, 13 Apr 2007 18:02:32 +0200 Subject: [PATCH 66/90] config.txt: Change pserver to server in description of gitcvs.* These variables apply to the SSH access as well, so don't use pserver here which might confuse users. Signed-off-by: Frank Lichtenheld Signed-off-by: Junio C Hamano --- Documentation/config.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/config.txt b/Documentation/config.txt index 38831bc906..e5be570481 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -411,11 +411,11 @@ gc.rerereunresolved:: The default is 15 days. See gitlink:git-rerere[1]. gitcvs.enabled:: - Whether the cvs pserver interface is enabled for this repository. + Whether the cvs server interface is enabled for this repository. See gitlink:git-cvsserver[1]. gitcvs.logfile:: - Path to a log file where the cvs pserver interface well... logs + Path to a log file where the cvs server interface well... logs various stuff. See gitlink:git-cvsserver[1]. gitcvs.allbinary:: From befc9c42043d67c2ccf5a8a0b660aac8e84bbeee Mon Sep 17 00:00:00 2001 From: Frank Lichtenheld Date: Fri, 13 Apr 2007 18:02:33 +0200 Subject: [PATCH 67/90] config.txt: Fix grammatical error in description of http.noEPSV s/doesn't/don't/ since "ftp servers" is plural Signed-off-by: Frank Lichtenheld Signed-off-by: Junio C Hamano --- Documentation/config.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/config.txt b/Documentation/config.txt index e5be570481..7e41ca6a0d 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -462,7 +462,7 @@ http.lowSpeedLimit, http.lowSpeedTime:: http.noEPSV:: A boolean which disables using of EPSV ftp command by curl. - This can helpful with some "poor" ftp servers which doesn't + This can helpful with some "poor" ftp servers which don't support EPSV mode. Can be overridden by the 'GIT_CURL_FTP_NO_EPSV' environment variable. Default is false (curl will use EPSV). From 1fa9bf362acbe2c939c246684ab0fe59aec74ba8 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 13 Apr 2007 14:34:18 -0700 Subject: [PATCH 68/90] git-quiltimport complaining yet still working There were two bugs: "stop_here" doesn't exist, but the bug that causes this code to trigger in the *first* place is the wrong use of "$dotest". It should be ".dotest" This is essentially the same bug introduced by 87ab7992, one was fixed with 0d38ab25 but this was somehow left behind. Signed-off-by: Junio C Hamano --- git-quiltimport.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/git-quiltimport.sh b/git-quiltimport.sh index edccd82755..018cc75bd0 100755 --- a/git-quiltimport.sh +++ b/git-quiltimport.sh @@ -73,9 +73,9 @@ mkdir $tmp_dir || exit 2 for patch_name in $(cat "$QUILT_PATCHES/series" | grep -v '^#'); do echo $patch_name (cat $QUILT_PATCHES/$patch_name | git-mailinfo "$tmp_msg" "$tmp_patch" > "$tmp_info") || exit 3 - test -s $dotest/patch || { + test -s .dotest/patch || { echo "Patch is empty. Was is split wrong?" - stop_here $this + exit 1 } # Parse the author information From 1bb88be99e4fdedcd5cc5292c11b566a00028deb Mon Sep 17 00:00:00 2001 From: Michael Spang Date: Sat, 14 Apr 2007 17:26:20 -0400 Subject: [PATCH 69/90] git-blame: Fix overrun in fake_working_tree_commit() git-blame would overflow commit->buffer when annotating files with long paths. Signed-off-by: Michael Spang Signed-off-by: Junio C Hamano --- builtin-blame.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin-blame.c b/builtin-blame.c index 60ec5354f1..bc86bda6c4 100644 --- a/builtin-blame.c +++ b/builtin-blame.c @@ -2041,7 +2041,7 @@ static struct commit *fake_working_tree_commit(const char *path, const char *con commit->buffer = xmalloc(400); ident = fmt_ident("Not Committed Yet", "not.committed.yet", NULL, 0); - sprintf(commit->buffer, + snprintf(commit->buffer, 400, "tree 0000000000000000000000000000000000000000\n" "parent %s\n" "author %s\n" From 61d6ed139ff572937c8ff342cfa16e40d2f9011e Mon Sep 17 00:00:00 2001 From: Jim Meyering Date: Tue, 10 Apr 2007 01:01:44 +0200 Subject: [PATCH 70/90] sscanf/strtoul: parse integers robustly * builtin-grep.c (strtoul_ui): Move function definition from here, to... * git-compat-util.h (strtoul_ui): ...here, with an added "base" parameter. * builtin-grep.c (cmd_grep): Update use of strtoul_ui to include base, "10". * builtin-update-index.c (read_index_info): Diagnose an invalid mode integer that is out of range or merely larger than INT_MAX. (cmd_update_index): Use strtoul_ui, not sscanf. * convert-objects.c (write_subdirectory): Likewise. Signed-off-by: Jim Meyering Signed-off-by: Junio C Hamano --- builtin-grep.c | 15 +-------------- builtin-update-index.c | 10 +++++++--- convert-objects.c | 2 +- git-compat-util.h | 13 +++++++++++++ 4 files changed, 22 insertions(+), 18 deletions(-) diff --git a/builtin-grep.c b/builtin-grep.c index 981f3d4d8e..e13cb31f2b 100644 --- a/builtin-grep.c +++ b/builtin-grep.c @@ -434,19 +434,6 @@ static const char emsg_missing_context_len[] = static const char emsg_missing_argument[] = "option requires an argument -%s"; -static int strtoul_ui(char const *s, unsigned int *result) -{ - unsigned long ul; - char *p; - - errno = 0; - ul = strtoul(s, &p, 10); - if (errno || *p || p == s || (unsigned int) ul != ul) - return -1; - *result = ul; - return 0; -} - int cmd_grep(int argc, const char **argv, const char *prefix) { int hit = 0; @@ -569,7 +556,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix) scan = arg + 1; break; } - if (strtoul_ui(scan, &num)) + if (strtoul_ui(scan, 10, &num)) die(emsg_invalid_context_len, scan); switch (arg[1]) { case 'A': diff --git a/builtin-update-index.c b/builtin-update-index.c index 71cef633c0..6ed61ebc57 100644 --- a/builtin-update-index.c +++ b/builtin-update-index.c @@ -227,6 +227,7 @@ static void read_index_info(int line_termination) char *path_name; unsigned char sha1[20]; unsigned int mode; + unsigned long ul; int stage; /* This reads lines formatted in one of three formats: @@ -249,9 +250,12 @@ static void read_index_info(int line_termination) if (buf.eof) break; - mode = strtoul(buf.buf, &ptr, 8); - if (ptr == buf.buf || *ptr != ' ') + errno = 0; + ul = strtoul(buf.buf, &ptr, 8); + if (ptr == buf.buf || *ptr != ' ' + || errno || (unsigned int) ul != ul) goto bad_line; + mode = ul; tab = strchr(ptr, '\t'); if (!tab || tab - ptr < 41) @@ -547,7 +551,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix) if (i+3 >= argc) die("git-update-index: --cacheinfo "); - if ((sscanf(argv[i+1], "%o", &mode) != 1) || + if ((strtoul_ui(argv[i+1], 8, &mode) != 1) || get_sha1_hex(argv[i+2], sha1) || add_cacheinfo(mode, sha1, argv[i+3], 0)) die("git-update-index: --cacheinfo" diff --git a/convert-objects.c b/convert-objects.c index 4809f9199f..cf03bcfe5a 100644 --- a/convert-objects.c +++ b/convert-objects.c @@ -88,7 +88,7 @@ static int write_subdirectory(void *buffer, unsigned long size, const char *base unsigned int mode; char *slash, *origpath; - if (!path || sscanf(buffer, "%o", &mode) != 1) + if (!path || strtoul_ui(buffer, 8, &mode) != 1) die("bad tree conversion"); mode = convert_mode(mode); path++; diff --git a/git-compat-util.h b/git-compat-util.h index 139fc19108..5f6a281b78 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -301,4 +301,17 @@ static inline int prefixcmp(const char *str, const char *prefix) return strncmp(str, prefix, strlen(prefix)); } +static inline int strtoul_ui(char const *s, int base, unsigned int *result) +{ + unsigned long ul; + char *p; + + errno = 0; + ul = strtoul(s, &p, base); + if (errno || *p || p == s || (unsigned int) ul != ul) + return -1; + *result = ul; + return 0; +} + #endif From 0b952a98f1dafc61e966237947e7836814fb74f5 Mon Sep 17 00:00:00 2001 From: "Luiz Fernando N. Capitulino" Date: Sun, 15 Apr 2007 18:40:31 -0300 Subject: [PATCH 71/90] ident.c: Use const qualifier for 'struct passwd' parameters Signed-off-by: Luiz Fernando N. Capitulino Signed-off-by: Junio C Hamano --- ident.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ident.c b/ident.c index bb03bddd34..b557ecd539 100644 --- a/ident.c +++ b/ident.c @@ -9,7 +9,7 @@ static char git_default_date[50]; -static void copy_gecos(struct passwd *w, char *name, int sz) +static void copy_gecos(const struct passwd *w, char *name, int sz) { char *src, *dst; int len, nlen; @@ -43,7 +43,7 @@ static void copy_gecos(struct passwd *w, char *name, int sz) } -static void copy_email(struct passwd *pw) +static void copy_email(const struct passwd *pw) { /* * Make up a fake email address From b0732115342d190d8c547ceaf5bf7a3534e3be5d Mon Sep 17 00:00:00 2001 From: "Luiz Fernando N. Capitulino" Date: Sun, 15 Apr 2007 15:51:29 -0300 Subject: [PATCH 72/90] ident.c: Use size_t (instead of int) to store sizes Signed-off-by: Luiz Fernando N. Capitulino Signed-off-by: Junio C Hamano --- ident.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/ident.c b/ident.c index b557ecd539..88e7f74e88 100644 --- a/ident.c +++ b/ident.c @@ -9,10 +9,10 @@ static char git_default_date[50]; -static void copy_gecos(const struct passwd *w, char *name, int sz) +static void copy_gecos(const struct passwd *w, char *name, size_t sz) { char *src, *dst; - int len, nlen; + size_t len, nlen; nlen = strlen(w->pw_name); @@ -49,7 +49,7 @@ static void copy_email(const struct passwd *pw) * Make up a fake email address * (name + '@' + hostname [+ '.' + domainname]) */ - int len = strlen(pw->pw_name); + size_t len = strlen(pw->pw_name); if (len > sizeof(git_default_email)/2) die("Your sysadmin must hate you!"); memcpy(git_default_email, pw->pw_name, len); @@ -95,9 +95,9 @@ static void setup_ident(void) datestamp(git_default_date, sizeof(git_default_date)); } -static int add_raw(char *buf, int size, int offset, const char *str) +static int add_raw(char *buf, size_t size, int offset, const char *str) { - int len = strlen(str); + size_t len = strlen(str); if (offset + len > size) return size; memcpy(buf + offset, str, len); @@ -131,9 +131,9 @@ static int crud(unsigned char c) * Copy over a string to the destination, but avoid special * characters ('\n', '<' and '>') and remove crud at the end */ -static int copy(char *buf, int size, int offset, const char *src) +static int copy(char *buf, size_t size, int offset, const char *src) { - int i, len; + size_t i, len; unsigned char c; /* Remove crud from the beginning.. */ From 5f2e1df5c9fa7856e5d7dda7d6c618ed08822b82 Mon Sep 17 00:00:00 2001 From: Alex Riesen Date: Mon, 16 Apr 2007 00:36:06 +0200 Subject: [PATCH 73/90] Document -g (--walk-reflogs) option of git-log Signed-off-by: Alex Riesen Signed-off-by: Junio C Hamano --- Documentation/git-log.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Documentation/git-log.txt b/Documentation/git-log.txt index 030edaf305..49bb539dea 100644 --- a/Documentation/git-log.txt +++ b/Documentation/git-log.txt @@ -46,6 +46,11 @@ include::pretty-formats.txt[] -p:: Show the change the commit introduces in a patch form. +-g, \--walk-reflogs:: + Show commits as they were recorded in the reflog. The log contains + a record about how the tip of a reference was changed. + See also gitlink:git-reflog[1]. + ...:: Show only commits that affect the specified paths. From 15320175355fc4770d9fb8b5a89ba8b36ef96246 Mon Sep 17 00:00:00 2001 From: Andrew Ruder Date: Mon, 16 Apr 2007 00:35:25 -0500 Subject: [PATCH 74/90] Add policy on user-interface changes Documentation/SubmittingPatches: Add note that all user interface changes should include associated documentation updates. Signed-off-by: Andrew Ruder Signed-off-by: Junio C Hamano --- Documentation/SubmittingPatches | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/SubmittingPatches b/Documentation/SubmittingPatches index 131bcff9b2..2386f496ee 100644 --- a/Documentation/SubmittingPatches +++ b/Documentation/SubmittingPatches @@ -22,6 +22,9 @@ Checklist (and a short version for the impatient): - provide additional information (which is unsuitable for the commit message) between the "---" and the diffstat - send the patch to the list _and_ the maintainer + - if you change, add, or remove a command line option or + make some other user interface change, the associated + documentation should be updated as well. Long version: From c7263d4d3d75d177f0ad8a8a730e1e3b401488c7 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Mon, 16 Apr 2007 08:51:11 +0300 Subject: [PATCH 75/90] Display the subject of the commit just made. Useful e.g. to figure out what I did from screen history, or to make sure subject line is short enough and makes sense on its own. Signed-off-by: Michael S. Tsirkin Signed-off-by: Junio C Hamano --- git-commit.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/git-commit.sh b/git-commit.sh index 9e0959aec0..f28fc24224 100755 --- a/git-commit.sh +++ b/git-commit.sh @@ -649,8 +649,9 @@ then fi if test -z "$quiet" then + commit=`git-diff-tree --always --shortstat --pretty="format:%h: %s"\ + --summary --root HEAD --` echo "Created${initial_commit:+ initial} commit $commit" - git-diff-tree --shortstat --summary --root --no-commit-id HEAD -- fi fi From b48caa20de7f62f648de7d3dbb0ceb462879e903 Mon Sep 17 00:00:00 2001 From: Steven Grimm Date: Mon, 16 Apr 2007 00:46:48 -0700 Subject: [PATCH 76/90] Add --quiet option to suppress output of "rm" commands for removed files. Signed-off-by: Steven Grimm Signed-off-by: Junio C Hamano --- Documentation/git-rm.txt | 4 ++++ builtin-rm.c | 9 ++++++--- t/t3600-rm.sh | 24 ++++++++++++++++++++++++ 3 files changed, 34 insertions(+), 3 deletions(-) diff --git a/Documentation/git-rm.txt b/Documentation/git-rm.txt index 6feebc0400..b051ccb6d6 100644 --- a/Documentation/git-rm.txt +++ b/Documentation/git-rm.txt @@ -47,6 +47,10 @@ OPTIONS the paths only from the index, leaving working tree files. +\--quiet:: + git-rm normally outputs one line (in the form of an "rm" command) + for each file removed. This option suppresses that output. + DISCUSSION ---------- diff --git a/builtin-rm.c b/builtin-rm.c index 8a0738f83d..d3de4b5452 100644 --- a/builtin-rm.c +++ b/builtin-rm.c @@ -10,7 +10,7 @@ #include "tree-walk.h" static const char builtin_rm_usage[] = -"git-rm [-f] [-n] [-r] [--cached] [--] ..."; +"git-rm [-f] [-n] [-r] [--cached] [--quiet] [--] ..."; static struct { int nr, alloc; @@ -104,7 +104,7 @@ static struct lock_file lock_file; int cmd_rm(int argc, const char **argv, const char *prefix) { int i, newfd; - int show_only = 0, force = 0, index_only = 0, recursive = 0; + int show_only = 0, force = 0, index_only = 0, recursive = 0, quiet = 0; const char **pathspec; char *seen; @@ -132,6 +132,8 @@ int cmd_rm(int argc, const char **argv, const char *prefix) force = 1; else if (!strcmp(arg, "-r")) recursive = 1; + else if (!strcmp(arg, "--quiet")) + quiet = 1; else usage(builtin_rm_usage); } @@ -187,7 +189,8 @@ int cmd_rm(int argc, const char **argv, const char *prefix) */ for (i = 0; i < list.nr; i++) { const char *path = list.name[i]; - printf("rm '%s'\n", path); + if (!quiet) + printf("rm '%s'\n", path); if (remove_file_from_cache(path)) die("git-rm: unable to remove %s", path); diff --git a/t/t3600-rm.sh b/t/t3600-rm.sh index e31cf93a00..da9da92180 100755 --- a/t/t3600-rm.sh +++ b/t/t3600-rm.sh @@ -84,6 +84,26 @@ test_expect_success \ 'When the rm in "git-rm -f" fails, it should not remove the file from the index' \ 'git-ls-files --error-unmatch baz' +test_expect_success '"rm" command printed' ' + echo frotz > test-file && + git add test-file && + git commit -m "add file for rm test" && + git rm test-file > rm-output && + test `egrep "^rm " rm-output | wc -l` = 1 && + rm -f test-file rm-output && + git commit -m "remove file from rm test" +' + +test_expect_success '"rm" command suppressed with --quiet' ' + echo frotz > test-file && + git add test-file && + git commit -m "add file for rm --quiet test" && + git rm --quiet test-file > rm-output && + test `wc -l < rm-output` = 0 && + rm -f test-file rm-output && + git commit -m "remove file from rm --quiet test" +' + # Now, failure cases. test_expect_success 'Re-add foo and baz' ' git add foo baz && @@ -154,4 +174,8 @@ test_expect_success 'Recursive with -r -f' ' ! test -d frotz ' +test_expect_failure 'Remove nonexistent file returns nonzero exit status' ' + git rm nonexistent +' + test_done From cb1881c6eeb021102a7a0ae87dbddfd52bf1f04f Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Mon, 16 Apr 2007 00:37:10 -0400 Subject: [PATCH 77/90] Documentation: minor edits of git-lost-found manpage Minor improvements to grammar and clarity of lost-found manpage. Signed-off-by: "J. Bruce Fields" Signed-off-by: Junio C Hamano --- Documentation/git-lost-found.txt | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/Documentation/git-lost-found.txt b/Documentation/git-lost-found.txt index f52a9d7f68..e48607f008 100644 --- a/Documentation/git-lost-found.txt +++ b/Documentation/git-lost-found.txt @@ -12,23 +12,22 @@ SYNOPSIS DESCRIPTION ----------- Finds dangling commits and tags from the object database, and -creates refs to them in .git/lost-found/ directory. Commits and -tags that dereference to commits go to .git/lost-found/commit -and others are stored in .git/lost-found/other directory. +creates refs to them in the .git/lost-found/ directory. Commits and +tags that dereference to commits are stored in .git/lost-found/commit, +and other objects are stored in .git/lost-found/other. OUTPUT ------ -One line description from the commit and tag found along with -their object name are printed on the standard output. - +Prints to standard output the object names and one-line descriptions +of any commits or tags found. EXAMPLE ------- -Suppose you run 'git tag -f' and mistyped the tag to overwrite. +Suppose you run 'git tag -f' and mistype the tag to overwrite. The ref to your tag is overwritten, but until you run 'git -prune', it is still there. +prune', the tag itself is still there. ------------ $ git lost-found @@ -36,15 +35,15 @@ $ git lost-found ... ------------ -Also you can use gitk to browse how they relate to each other -and existing (probably old) tags. +Also you can use gitk to browse how any tags found relate to each +other. ------------ $ gitk $(cd .git/lost-found/commit && echo ??*) ------------ -After making sure that it is the object you are looking for, you -can reconnect it to your regular .git/refs hierarchy. +After making sure you know which the object is the tag you are looking +for, you can reconnect it to your regular .git/refs hierarchy. ------------ $ git cat-file -t 1ef2b196 From 40c8279f9b4f1c37f5e994c1c053b69446133559 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Mon, 16 Apr 2007 00:37:11 -0400 Subject: [PATCH 78/90] Documentation: clarify git-checkout -f, minor editing "Force a re-read of everything" doesn't mean much to me. Also some minor grammar fixes. Signed-off-by: "J. Bruce Fields" Signed-off-by: Junio C Hamano --- Documentation/git-checkout.txt | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt index f5b2d5017b..b889688b40 100644 --- a/Documentation/git-checkout.txt +++ b/Documentation/git-checkout.txt @@ -23,9 +23,9 @@ options, which will be passed to `git branch`. When are given, this command does *not* switch branches. It updates the named paths in the working tree from -the index file (i.e. it runs `git-checkout-index -f -u`), or a -named commit. In -this case, `-f` and `-b` options are meaningless and giving +the index file (i.e. it runs `git-checkout-index -f -u`), or +from a named commit. In +this case, the `-f` and `-b` options are meaningless and giving either of them results in an error. argument can be used to specify a specific tree-ish (i.e. commit, tag or tree) to update the index for the given paths before updating the @@ -38,7 +38,8 @@ OPTIONS Quiet, supress feedback messages. -f:: - Force a re-read of everything. + Proceed even if the index or the working tree differs + from HEAD. This is used to throw away local changes. -b:: Create a new branch named and start it at From b71083043c66b241713ff0698f113eb7c8bc9a90 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Mon, 16 Apr 2007 00:37:12 -0400 Subject: [PATCH 79/90] Documentation: clarify track/no-track option. Fix the description of the --no-track option so it no longer says the opposite of what was intended. Also mention branch.autosetupmerge explicitly. Signed-off-by: "J. Bruce Fields" Signed-off-by: Junio C Hamano --- Documentation/git-checkout.txt | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt index b889688b40..4f2e847dc3 100644 --- a/Documentation/git-checkout.txt +++ b/Documentation/git-checkout.txt @@ -49,13 +49,17 @@ OPTIONS --track:: When -b is given and a branch is created off a remote branch, - setup so that git-pull will automatically retrieve data from - the remote branch. + set up configuration so that git-pull will automatically + retrieve data from the remote branch. Set the + branch.autosetupmerge configuration variable to true if you + want git-checkout and git-branch to always behave as if + '--track' were given. --no-track:: When -b is given and a branch is created off a remote branch, - force that git-pull will automatically retrieve data from - the remote branch independent of the configuration settings. + set up configuration so that git-pull will not retrieve data + from the remote branch, ignoring the branch.autosetupmerge + configuration variable. -l:: Create the new branch's ref log. This activates recording of From 4f752407964a81cf2ba5750343a7f0daf54c19b8 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Mon, 16 Apr 2007 00:37:13 -0400 Subject: [PATCH 80/90] user-manual: fix discussion of default clone The name "master" isn't actually quite so special. Also, fix some bad grammar. Signed-off-by: "J. Bruce Fields" Signed-off-by: Junio C Hamano --- Documentation/user-manual.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt index d43d2377ec..49e936fa10 100644 --- a/Documentation/user-manual.txt +++ b/Documentation/user-manual.txt @@ -298,9 +298,9 @@ $ git branch * master ------------------------------------------------ -A freshly cloned repository contains a single branch head, named -"master", and working directory is initialized to the state of -the project referred to by "master". +A freshly cloned repository contains a single branch head, by default +named "master", with the working directory initialized to the state of +the project referred to by that branch head. Most projects also use <>. Tags, like heads, are references into the project's history, and can be listed using the From 72a76c955bcbe6370db7dc34b962851de189e650 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Mon, 16 Apr 2007 00:37:14 -0400 Subject: [PATCH 81/90] user-manual: detached HEAD Add a brief mention of detached HEADs and .git/HEAD. Signed-off-by: "J. Bruce Fields" Signed-off-by: Junio C Hamano --- Documentation/user-manual.txt | 44 +++++++++++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt index 49e936fa10..bff072fb90 100644 --- a/Documentation/user-manual.txt +++ b/Documentation/user-manual.txt @@ -495,8 +495,48 @@ git checkout -b :: create a new branch referencing , and check it out. -It is also useful to know that the special symbol "HEAD" can always -be used to refer to the current branch. +The special symbol "HEAD" can always be used to refer to the current +branch. In fact, git uses a file named "HEAD" in the .git directory to +remember which branch is current: + +------------------------------------------------ +$ cat .git/HEAD +ref: refs/heads/master +------------------------------------------------ + +Examining an old version without creating a new branch +------------------------------------------------------ + +The git-checkout command normally expects a branch head, but will also +accept an arbitrary commit; for example, you can check out the commit +referenced by a tag: + +------------------------------------------------ +$ git checkout v2.6.17 +Note: moving to "v2.6.17" which isn't a local branch +If you want to create a new branch from this checkout, you may do so +(now or later) by using -b with the checkout command again. Example: + git checkout -b +HEAD is now at 427abfa... Linux v2.6.17 +------------------------------------------------ + +The HEAD then refers to the SHA1 of the commit instead of to a branch, +and git branch shows that you are no longer on a branch: + +------------------------------------------------ +$ cat .git/HEAD +427abfa28afedffadfca9dd8b067eb6d36bac53f +git branch +* (no branch) + master +------------------------------------------------ + +In this case we say that the HEAD is "detached". + +This can be an easy way to check out a particular version without having +to make up a name for a new branch. However, keep in mind that when you +switch away from the (for example, by checking out something else), you +can lose track of what the HEAD used to point to. Examining branches from a remote repository ------------------------------------------- From a536b08b491dbb7565fb6ce109dbcf1997f9ab7a Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Mon, 16 Apr 2007 00:37:15 -0400 Subject: [PATCH 82/90] user-manual: start revising "internals" chapter Minor revisions, cross-references. Signed-off-by: "J. Bruce Fields" Signed-off-by: Junio C Hamano --- Documentation/user-manual.txt | 38 ++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt index bff072fb90..18d3bc7297 100644 --- a/Documentation/user-manual.txt +++ b/Documentation/user-manual.txt @@ -2315,8 +2315,8 @@ options mentioned above. Git internals ============= -There are two object abstractions: the "object database", and the -"current directory cache" aka "index". +Git depends on two fundamental abstractions: the "object database", and +the "current directory cache" aka "index". The Object Database ------------------- @@ -2331,22 +2331,23 @@ All objects have a statically determined "type" aka "tag", which is determined at object creation time, and which identifies the format of the object (i.e. how it is used, and how it can refer to other objects). There are currently four different object types: "blob", -"tree", "commit" and "tag". +"tree", "commit", and "tag". -A "blob" object cannot refer to any other object, and is, like the type -implies, a pure storage object containing some user data. It is used to -actually store the file data, i.e. a blob object is associated with some -particular version of some file. +A <> cannot refer to any other object, +and is, as the name implies, a pure storage object containing some +user data. It is used to actually store the file data, i.e. a blob +object is associated with some particular version of some file. -A "tree" object is an object that ties one or more "blob" objects into a -directory structure. In addition, a tree object can refer to other tree -objects, thus creating a directory hierarchy. +A <> is an object that ties one or more +"blob" objects into a directory structure. In addition, a tree object +can refer to other tree objects, thus creating a directory hierarchy. -A "commit" object ties such directory hierarchies together into -a DAG of revisions - each "commit" is associated with exactly one tree -(the directory hierarchy at the time of the commit). In addition, a -"commit" refers to one or more "parent" commit objects that describe the -history of how we arrived at that directory hierarchy. +A <> ties such directory hierarchies +together into a <> of revisions - each +"commit" is associated with exactly one tree (the directory hierarchy at +the time of the commit). In addition, a "commit" refers to one or more +"parent" commit objects that describe the history of how we arrived at +that directory hierarchy. As a special case, a commit object with no parents is called the "root" object, and is the point of an initial project commit. Each project @@ -2356,9 +2357,10 @@ has two or more separate roots as its ultimate parents, that's probably just going to confuse people. So aim for the notion of "one root object per project", even if git itself does not enforce that. -A "tag" object symbolically identifies and can be used to sign other -objects. It contains the identifier and type of another object, a -symbolic name (of course!) and, optionally, a signature. +A <> symbolically identifies and can be +used to sign other objects. It contains the identifier and type of +another object, a symbolic name (of course!) and, optionally, a +signature. Regardless of object type, all objects share the following characteristics: they are all deflated with zlib, and have a header From 25d9f3fa2d493bc769e39d18bf843cf11c235062 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Mon, 16 Apr 2007 00:37:16 -0400 Subject: [PATCH 83/90] user-manual: use detached head when rewriting history This is slightly simpler if we use a detached head. And it's probably good to have another example that uses this feature. Signed-off-by: "J. Bruce Fields" Signed-off-by: Junio C Hamano --- Documentation/user-manual.txt | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt index 18d3bc7297..9c4c41df5a 100644 --- a/Documentation/user-manual.txt +++ b/Documentation/user-manual.txt @@ -504,6 +504,7 @@ $ cat .git/HEAD ref: refs/heads/master ------------------------------------------------ +[[detached-head]] Examining an old version without creating a new branch ------------------------------------------------------ @@ -2055,22 +2056,22 @@ $ git tag bad mywork~5 (Either gitk or git-log may be useful for finding the commit.) -Then check out a new branch at that commit, edit it, and rebase the rest of -the series on top of it: +Then check out that commit, edit it, and rebase the rest of the series +on top of it (note that we could check out the commit on a temporary +branch, but instead we're using a <>): ------------------------------------------------- -$ git checkout -b TMP bad +$ git checkout bad $ # make changes here and update the index $ git commit --amend -$ git rebase --onto TMP bad mywork +$ git rebase --onto HEAD bad mywork ------------------------------------------------- -When you're done, you'll be left with mywork checked out, with the top patches -on mywork reapplied on top of the modified commit you created in TMP. You can +When you're done, you'll be left with mywork checked out, with the top +patches on mywork reapplied on top of your modified commit. You can then clean up with ------------------------------------------------- -$ git branch -d TMP $ git tag -d bad ------------------------------------------------- From 5c19f244c3ca1ef5bf419bcc822c3b8184d24f8f Mon Sep 17 00:00:00 2001 From: Andrew Ruder Date: Mon, 16 Apr 2007 02:21:31 -0500 Subject: [PATCH 84/90] Update git-am documentation Documentation/git-am.txt missing several short versions of options. Added documentation for --resolvemsg= command-line option. Signed-off-by: Andrew Ruder Signed-off-by: Junio C Hamano --- Documentation/git-am.txt | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt index 148ce40568..f0405a35e9 100644 --- a/Documentation/git-am.txt +++ b/Documentation/git-am.txt @@ -26,18 +26,18 @@ OPTIONS The list of mailbox files to read patches from. If you do not supply this argument, reads from the standard input. ---signoff:: +-s, --signoff:: Add `Signed-off-by:` line to the commit message, using the committer identity of yourself. ---dotest=:: +-d=, --dotest=:: Instead of `.dotest` directory, use as a working area to store extracted patches. ---keep:: +-k, --keep:: Pass `-k` flag to `git-mailinfo` (see gitlink:git-mailinfo[1]). ---utf8:: +-u, --utf8:: Pass `-u` flag to `git-mailinfo` (see gitlink:git-mailinfo[1]). The proposed commit log message taken from the e-mail are re-coded into UTF-8 encoding (configuration variable @@ -48,14 +48,14 @@ This was optional in prior versions of git, but now it is the default. You could use `--no-utf8` to override this. --no-utf8:: - Do not pass `-u` flag to `git-mailinfo` (see + Pass `-n` flag to `git-mailinfo` (see gitlink:git-mailinfo[1]). ---binary:: +-b, --binary:: Pass `--allow-binary-replacement` flag to `git-apply` (see gitlink:git-apply[1]). ---3way:: +-3, --3way:: When the patch does not apply cleanly, fall back on 3-way merge, if the patch records the identity of blobs it is supposed to apply to, and we have those blobs @@ -73,10 +73,10 @@ default. You could use `--no-utf8` to override this. These flags are passed to the `git-apply` program that applies the patch. ---interactive:: +-i, --interactive:: Run interactively, just like git-applymbox. ---resolved:: +-r, --resolved:: After a patch failure (e.g. attempting to apply conflicting patch), the user has applied it by hand and the index file stores the result of the application. @@ -84,6 +84,13 @@ default. You could use `--no-utf8` to override this. extracted from the e-mail message and the current index file, and continue. +--resolvemsg=:: + When a patch failure occurs, will be printed + to the screen before exiting. This overrides the + standard message informing you to use `--resolved` + or `--skip` to handle the failure. This is solely + for internal use between `git-rebase` and `git-am`. + DISCUSSION ---------- From 982f65ace6c2be08b881318ca3241736dddd8aee Mon Sep 17 00:00:00 2001 From: Andrew Ruder Date: Mon, 16 Apr 2007 01:40:06 -0500 Subject: [PATCH 85/90] Update git-applymbox documentation Documentation/git-applymbox.txt: updating -u documentation to include fact that it encodes to the i18n.commitencoding setting, not just utf-8. Added documentation of -n option to pass -n to git-mailinfo. Signed-off-by: Andrew Ruder Signed-off-by: Junio C Hamano --- Documentation/git-applymbox.txt | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/Documentation/git-applymbox.txt b/Documentation/git-applymbox.txt index 95dc65a583..3bc92d8cf1 100644 --- a/Documentation/git-applymbox.txt +++ b/Documentation/git-applymbox.txt @@ -42,14 +42,20 @@ OPTIONS and the current tree. -u:: - The commit log message, author name and author email are - taken from the e-mail, and after minimally decoding MIME - transfer encoding, re-coded in UTF-8 by transliterating - them. This used to be optional but now it is the default. + Pass `-u` flag to `git-mailinfo` (see gitlink:git-mailinfo[1]). + The proposed commit log message taken from the e-mail + are re-coded into UTF-8 encoding (configuration variable + `i18n.commitencoding` can be used to specify project's + preferred encoding if it is not UTF-8). This used to be + optional but now it is the default. + Note that the patch is always used as-is without charset conversion, even with this flag. +-n:: + Pass `-n` flag to `git-mailinfo` (see + gitlink:git-mailinfo[1]). + -c .dotest/:: When the patch contained in an e-mail does not cleanly apply, the command exits with an error message. The From 0b9a9dd00a4c08b09d5661942b31c7ee590626b7 Mon Sep 17 00:00:00 2001 From: Andrew Ruder Date: Mon, 16 Apr 2007 01:20:40 -0500 Subject: [PATCH 86/90] Update git-apply documentation Document -v (short form of --verbose). Redo usage to not wrap on 80 column terminal with typical settings. Signed-off-by: Andrew Ruder Signed-off-by: Junio C Hamano --- Documentation/git-apply.txt | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Documentation/git-apply.txt b/Documentation/git-apply.txt index 065ba1bf24..3bd2c995da 100644 --- a/Documentation/git-apply.txt +++ b/Documentation/git-apply.txt @@ -9,11 +9,12 @@ git-apply - Apply a patch on a git index file and a working tree SYNOPSIS -------- [verse] -'git-apply' [--stat] [--numstat] [--summary] [--check] [--index] [--apply] - [--no-add] [--index-info] [--allow-binary-replacement | --binary] - [-R | --reverse] [--reject] [-z] [-pNUM] [-CNUM] [--inaccurate-eof] - [--whitespace=] [--exclude=PATH] - [--cached] [--verbose] [...] +'git-apply' [--stat] [--numstat] [--summary] [--check] [--index] + [--apply] [--no-add] [--index-info] [-R | --reverse] + [--allow-binary-replacement | --binary] [--reject] [-z] + [-pNUM] [-CNUM] [--inaccurate-eof] [--cached] + [--whitespace=] + [--exclude=PATH] [--verbose] [...] DESCRIPTION ----------- @@ -158,7 +159,7 @@ discouraged. correctly. This option adds support for applying such patches by working around this bug. ---verbose:: +-v, --verbose:: Report progress to stderr. By default, only a message about the current patch being applied will be printed. This option will cause additional information to be reported. From 635f4a30f0f73fcd3ed29c8d70be334689cc4082 Mon Sep 17 00:00:00 2001 From: Andrew Ruder Date: Mon, 16 Apr 2007 01:20:34 -0500 Subject: [PATCH 87/90] Update git-annotate/git-blame documentation Moved options that pertained to both git-blame and git-annotate to a common file blame-options.txt. builtin-blame.c: Removed --compatibility, --long, --time from the short usage as they are not handled in the code. Documentation/git-blame.txt: Removed common options to git-annotate. Added documentation for --score-debug. Removed --compatibility. Adjusted usage at top to not wrap on 80 columns. Documentation/git-annotate.txt: Using common options blame-options.txt. Documentation/blame-options.txt: Added -b note about associated config option, added --root note about associated config option, added documentation for --show-stats. Removed --long, --time, --rev-file as those options do not really exist. Added documentation for -M/-C taking an optional score argument for detection of moved lines. Signed-off-by: Andrew Ruder Signed-off-by: Junio C Hamano --- Documentation/blame-options.txt | 67 +++++++++++++++++++++++++++++++++ Documentation/git-annotate.txt | 15 +------- Documentation/git-blame.txt | 64 +++++++------------------------ builtin-blame.c | 6 +-- 4 files changed, 85 insertions(+), 67 deletions(-) create mode 100644 Documentation/blame-options.txt diff --git a/Documentation/blame-options.txt b/Documentation/blame-options.txt new file mode 100644 index 0000000000..331f161c77 --- /dev/null +++ b/Documentation/blame-options.txt @@ -0,0 +1,67 @@ +-b:: + Show blank SHA-1 for boundary commits. This can also + be controlled via the `blame.blankboundary` config option. + +--root:: + Do not treat root commits as boundaries. This can also be + controlled via the `blame.showroot` config option. + +--show-stats:: + Include additional statistics at the end of blame output. + +-L n,m:: + Annotate only the specified line range (lines count from 1). + +-l:: + Show long rev (Default: off). + +-t:: + Show raw timestamp (Default: off). + +-S :: + Use revs from revs-file instead of calling gitlink:git-rev-list[1]. + +-p, --porcelain:: + Show in a format designed for machine consumption. + +--incremental:: + Show the result incrementally in a format designed for + machine consumption. + +--contents :: + When is not specified, the command annotates the + changes starting backwards from the working tree copy. + This flag makes the command pretend as if the working + tree copy has the contents of he named file (specify + `-` to make the command read from the standard input). + +-M||:: + Detect moving lines in the file as well. When a commit + moves a block of lines in a file (e.g. the original file + has A and then B, and the commit changes it to B and + then A), traditional 'blame' algorithm typically blames + the lines that were moved up (i.e. B) to the parent and + assigns blame to the lines that were moved down (i.e. A) + to the child commit. With this option, both groups of lines + are blamed on the parent. + + is optional but it is the lower bound on the number of + alphanumeric characters that git must detect as moving + within a file for it to associate those lines with the parent + commit. + +-C||:: + In addition to `-M`, detect lines copied from other + files that were modified in the same commit. This is + useful when you reorganize your program and move code + around across files. When this option is given twice, + the command looks for copies from all other files in the + parent for the commit that creates the file in addition. + + is optional but it is the lower bound on the number of + alphanumeric characters that git must detect as moving + between files for it to associate those lines with the parent + commit. + +-h, --help:: + Show help message. diff --git a/Documentation/git-annotate.txt b/Documentation/git-annotate.txt index 7baf73111b..02dc4740d0 100644 --- a/Documentation/git-annotate.txt +++ b/Documentation/git-annotate.txt @@ -16,20 +16,7 @@ which introduced the line. Optionally annotate from a given revision. OPTIONS ------- --l, --long:: - Show long rev (Defaults off). - --t, --time:: - Show raw timestamp (Defaults off). - --r, --rename:: - Follow renames (Defaults on). - --S, --rev-file :: - Use revs from revs-file instead of calling git-rev-list. - --h, --help:: - Show help message. +include::blame-options.txt[] SEE ALSO -------- diff --git a/Documentation/git-blame.txt b/Documentation/git-blame.txt index 5c9888d014..8f9439a6dd 100644 --- a/Documentation/git-blame.txt +++ b/Documentation/git-blame.txt @@ -8,8 +8,9 @@ git-blame - Show what revision and author last modified each line of a file SYNOPSIS -------- [verse] -'git-blame' [-c] [-l] [-t] [-f] [-n] [-p] [--incremental] [-L n,m] [-S ] - [-M] [-C] [-C] [--since=] [ | --contents ] [--] +'git-blame' [-c] [-l] [-t] [-f] [-n] [-p] [--incremental] [-L n,m] + [-S ] [-M] [-C] [-C] [--since=] + [ | --contents ] [--] DESCRIPTION ----------- @@ -37,20 +38,19 @@ ea4c7f9bf69e781dd0cd88d2bccb2bf5cc15c9a7 git-blame: Make the output OPTIONS ------- --c, --compatibility:: +include::blame-options.txt[] + +-c:: Use the same output mode as gitlink:git-annotate[1] (Default: off). --L n,m:: - Annotate only the specified line range (lines count from 1). - --l, --long:: - Show long rev (Default: off). - --t, --time:: - Show raw timestamp (Default: off). - --S, --rev-file :: - Use revs from revs-file instead of calling gitlink:git-rev-list[1]. +--score-debug:: + Include debugging information related to the movement of + lines between files (see `-C`) and lines moved within a + file (see `-M`). The first number listed is the score. + This is the number of alphanumeric characters detected + to be moved between or within files. This must be above + a certain threshold for git-blame to consider those lines + of code to have been moved. -f, --show-name:: Show filename in the original commit. By default @@ -60,42 +60,6 @@ OPTIONS -n, --show-number:: Show line number in the original commit (Default: off). --p, --porcelain:: - Show in a format designed for machine consumption. - ---incremental:: - Show the result incrementally in a format designed for - machine consumption. - ---contents :: - When is not specified, the command annotates the - changes starting backwards from the working tree copy. - This flag makes the command pretend as if the working - tree copy has the contents of he named file (specify - `-` to make the command read from the standard input). - --M:: - Detect moving lines in the file as well. When a commit - moves a block of lines in a file (e.g. the original file - has A and then B, and the commit changes it to B and - then A), traditional 'blame' algorithm typically blames - the lines that were moved up (i.e. B) to the parent and - assigns blame to the lines that were moved down (i.e. A) - to the child commit. With this option, both groups of - lines are blamed on the parent. - --C:: - In addition to `-M`, detect lines copied from other - files that were modified in the same commit. This is - useful when you reorganize your program and move code - around across files. When this option is given twice, - the command looks for copies from all other files in the - parent for the commit that creates the file in addition. - --h, --help:: - Show help message. - - THE PORCELAIN FORMAT -------------------- diff --git a/builtin-blame.c b/builtin-blame.c index bc86bda6c4..8919b028e6 100644 --- a/builtin-blame.c +++ b/builtin-blame.c @@ -19,11 +19,11 @@ static char blame_usage[] = "git-blame [-c] [-l] [-t] [-f] [-n] [-p] [-L n,m] [-S ] [-M] [-C] [-C] [--contents ] [--incremental] [commit] [--] file\n" -" -c, --compatibility Use the same output mode as git-annotate (Default: off)\n" +" -c Use the same output mode as git-annotate (Default: off)\n" " -b Show blank SHA-1 for boundary commits (Default: off)\n" -" -l, --long Show long commit SHA1 (Default: off)\n" +" -l Show long commit SHA1 (Default: off)\n" " --root Do not treat root commits as boundaries (Default: off)\n" -" -t, --time Show raw timestamp (Default: off)\n" +" -t Show raw timestamp (Default: off)\n" " -f, --show-name Show original filename (Default: auto)\n" " -n, --show-number Show original linenumber (Default: off)\n" " -p, --porcelain Show in a format designed for machine consumption\n" From 9474eda6c239bae68a095013ee50a8c90dd0f182 Mon Sep 17 00:00:00 2001 From: Steven Grimm Date: Mon, 16 Apr 2007 01:17:32 -0700 Subject: [PATCH 88/90] git-rm: Trivial fix for a comment typo. Signed-off-by: Steven Grimm Signed-off-by: Junio C Hamano --- builtin-rm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin-rm.c b/builtin-rm.c index d3de4b5452..b77b058e38 100644 --- a/builtin-rm.c +++ b/builtin-rm.c @@ -170,7 +170,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix) * must match; but the file can already been removed, since * this sequence is a natural "novice" way: * - * rm F; git fm F + * rm F; git rm F * * Further, if HEAD commit exists, "diff-index --cached" must * report no changes unless forced. From 5946d88a349407f2830b4d186201076b80a7cce4 Mon Sep 17 00:00:00 2001 From: Gerrit Pape Date: Mon, 16 Apr 2007 08:30:42 +0000 Subject: [PATCH 89/90] variable $projectdesc needs to be set before checking against unchanged default. Signed-off-by: Gerrit Pape Signed-off-by: Junio C Hamano --- templates/hooks--update | 1 + 1 file changed, 1 insertion(+) diff --git a/templates/hooks--update b/templates/hooks--update index 0ff03309e6..0dcb1adb13 100644 --- a/templates/hooks--update +++ b/templates/hooks--update @@ -34,6 +34,7 @@ fi allowunannotated=$(git-repo-config --bool hooks.allowunannotated) # check for no description +projectdesc=$(sed -e '1p' "$GIT_DIR/description") if [ -z "$projectdesc" -o "$projectdesc" = "Unnamed repository; edit this file to name it for gitweb" ]; then echo "*** Project description file hasn't been set" >&2 exit 1 From 91776491da19f1b72e1cd192c9ea42bb1aae4415 Mon Sep 17 00:00:00 2001 From: Gerrit Pape Date: Mon, 16 Apr 2007 08:31:35 +0000 Subject: [PATCH 90/90] Have sample update hook not refuse deleting a branch through push. source ref might be 0000...0000 to delete a branch through git-push, 'git push :'. The update hook should not decline this. Signed-off-by: Gerrit Pape Signed-off-by: Junio C Hamano --- templates/hooks--update | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/templates/hooks--update b/templates/hooks--update index 0dcb1adb13..9d3795c6d0 100644 --- a/templates/hooks--update +++ b/templates/hooks--update @@ -41,7 +41,12 @@ if [ -z "$projectdesc" -o "$projectdesc" = "Unnamed repository; edit this file t fi # --- Check types -newrev_type=$(git-cat-file -t $newrev) +# if $newrev is 0000...0000, it's a commit to delete a branch +if [ -z "${newrev##0*}" ]; then + newrev_type=commit +else + newrev_type=$(git-cat-file -t $newrev) +fi case "$refname","$newrev_type" in refs/tags/*,commit)