From 2266bf27b354a08522406eff916898b6300ddfa0 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 18 Jan 2007 23:25:54 -0800 Subject: [PATCH 01/63] for_each_reflog_ent: do not leak FILE * The callback function can signal an early return by returning non-zero, but the function leaked the FILE * opened on the reflog when doing so. Signed-off-by: Junio C Hamano --- refs.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/refs.c b/refs.c index 7d858637c4..87efa38c7e 100644 --- a/refs.c +++ b/refs.c @@ -1110,6 +1110,7 @@ int for_each_reflog_ent(const char *ref, each_reflog_ent_fn fn, void *cb_data) const char *logfile; FILE *logfp; char buf[1024]; + int ret = 0; logfile = git_path("logs/%s", ref); logfp = fopen(logfile, "r"); @@ -1119,7 +1120,7 @@ int for_each_reflog_ent(const char *ref, each_reflog_ent_fn fn, void *cb_data) unsigned char osha1[20], nsha1[20]; char *email_end, *message; unsigned long timestamp; - int len, ret, tz; + int len, tz; /* old SP new SP name SP time TAB msg LF */ len = strlen(buf); @@ -1140,9 +1141,9 @@ int for_each_reflog_ent(const char *ref, each_reflog_ent_fn fn, void *cb_data) message += 7; ret = fn(osha1, nsha1, buf+82, timestamp, tz, message, cb_data); if (ret) - return ret; + break; } fclose(logfp); - return 0; + return ret; } From cb48cb585f3bed37467565ed35889726568a442d Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 19 Jan 2007 00:39:32 -0800 Subject: [PATCH 02/63] refs.c::read_ref_at(): fix bogus munmap() call. The code uses mmap() to read reflog data, but moves the pointer around while reading, and uses that updated pointer in the call to munmap(). Signed-off-by: Junio C Hamano --- refs.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/refs.c b/refs.c index 87efa38c7e..6310cf46e0 100644 --- a/refs.c +++ b/refs.c @@ -1025,6 +1025,7 @@ int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char * struct stat st; unsigned long date; unsigned char logged_sha1[20]; + void *log_mapped; logfile = git_path("logs/%s", ref); logfd = open(logfile, O_RDONLY, 0); @@ -1033,7 +1034,8 @@ int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char * fstat(logfd, &st); if (!st.st_size) die("Log %s is empty.", logfile); - logdata = xmmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, logfd, 0); + log_mapped = xmmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, logfd, 0); + logdata = log_mapped; close(logfd); lastrec = NULL; @@ -1078,7 +1080,7 @@ int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char * logfile, show_rfc2822_date(date, tz)); } } - munmap((void*)logdata, st.st_size); + munmap(log_mapped, st.st_size); return 0; } lastrec = rec; @@ -1095,7 +1097,7 @@ int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char * tz = strtoul(tz_c, NULL, 10); if (get_sha1_hex(logdata, sha1)) die("Log %s is corrupt.", logfile); - munmap((void*)logdata, st.st_size); + munmap(log_mapped, st.st_size); if (at_time) fprintf(stderr, "warning: Log %s only goes back to %s.\n", logfile, show_rfc2822_date(date, tz)); From be93fc088f3c6f78c3dd1b10544287129d066512 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 19 Jan 2007 11:33:27 -0800 Subject: [PATCH 03/63] Documentation: generated cmds-*.txt does not depend on git.txt Pointed out by Santi. Signed-off-by: Junio C Hamano --- Documentation/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/Makefile b/Documentation/Makefile index 96755ad687..97ea133b43 100644 --- a/Documentation/Makefile +++ b/Documentation/Makefile @@ -78,7 +78,7 @@ cmds_txt = cmds-ancillaryinterrogators.txt \ cmds-plumbingmanipulators.txt \ cmds-synchingrepositories.txt -$(cmds_txt): cmd-list.perl $(MAN1_TXT) $(MAN7_TXT) +$(cmds_txt): cmd-list.perl $(MAN1_TXT) perl ./cmd-list.perl git.7 git.html: git.txt core-intro.txt From 89bf207758530fd151895680f7236ce626feea9c Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 18 Jan 2007 22:32:38 -0800 Subject: [PATCH 04/63] Documentation/git.txt: command re-classification This adds two new classes (pure-helpers and "Interacting with Others") to the command list in the main manual page. The latter class is primarily about foreign SCM interface and is placed before low-level (plumbing) commands. Also it promotes a handful commands to mainporcelain category while demoting some others. Signed-off-by: Junio C Hamano --- Documentation/Makefile | 5 +- Documentation/cmd-list.perl | 94 +++++++++++++++++++------------------ Documentation/git.txt | 35 +++++++++++++- 3 files changed, 86 insertions(+), 48 deletions(-) diff --git a/Documentation/Makefile b/Documentation/Makefile index 97ea133b43..5314068d32 100644 --- a/Documentation/Makefile +++ b/Documentation/Makefile @@ -76,7 +76,10 @@ cmds_txt = cmds-ancillaryinterrogators.txt \ cmds-mainporcelain.txt \ cmds-plumbinginterrogators.txt \ cmds-plumbingmanipulators.txt \ - cmds-synchingrepositories.txt + cmds-synchingrepositories.txt \ + cmds-synchelpers.txt \ + cmds-purehelpers.txt \ + cmds-foreignscminterface.txt $(cmds_txt): cmd-list.perl $(MAN1_TXT) perl ./cmd-list.perl diff --git a/Documentation/cmd-list.perl b/Documentation/cmd-list.perl index 5680dcbf9a..744db82413 100755 --- a/Documentation/cmd-list.perl +++ b/Documentation/cmd-list.perl @@ -45,7 +45,10 @@ for my $cat (qw(ancillaryinterrogators mainporcelain plumbinginterrogators plumbingmanipulators - synchingrepositories)) { + synchingrepositories + foreignscminterface + purehelpers + synchelpers)) { my $out = "cmds-$cat.txt"; open O, '>', "$out+" or die "Cannot open output file $out+"; for (@{$cmds{$cat}}) { @@ -59,10 +62,10 @@ __DATA__ git-add mainporcelain git-am mainporcelain git-annotate ancillaryinterrogators -git-applymbox mainporcelain -git-applypatch ancillarymanipulators +git-applymbox ancillaryinterrogators +git-applypatch purehelpers git-apply plumbingmanipulators -git-archimport ancillarymanipulators +git-archimport foreignscminterface git-archive mainporcelain git-bisect mainporcelain git-blame ancillaryinterrogators @@ -70,7 +73,7 @@ git-branch mainporcelain git-cat-file plumbinginterrogators git-checkout-index plumbingmanipulators git-checkout mainporcelain -git-check-ref-format ancillaryinterrogators +git-check-ref-format purehelpers git-cherry ancillaryinterrogators git-cherry-pick mainporcelain git-clean mainporcelain @@ -79,11 +82,11 @@ git-commit mainporcelain git-commit-tree plumbingmanipulators git-convert-objects ancillarymanipulators git-count-objects ancillaryinterrogators -git-cvsexportcommit ancillarymanipulators -git-cvsimport ancillarymanipulators -git-cvsserver ancillarymanipulators -git-daemon ancillaryinterrogators -git-describe plumbinginterrogators +git-cvsexportcommit foreignscminterface +git-cvsimport foreignscminterface +git-cvsserver foreignscminterface +git-daemon synchingrepositories +git-describe mainporcelain git-diff-files plumbinginterrogators git-diff-index plumbinginterrogators git-diff mainporcelain @@ -91,34 +94,34 @@ git-diff-stages plumbinginterrogators git-diff-tree plumbinginterrogators git-fetch mainporcelain git-fetch-pack synchingrepositories -git-fmt-merge-msg ancillaryinterrogators +git-fmt-merge-msg purehelpers git-for-each-ref plumbinginterrogators git-format-patch mainporcelain -git-fsck-objects plumbinginterrogators -git-gc ancillarymanipulators +git-fsck-objects ancillaryinterrogators +git-gc mainporcelain git-get-tar-commit-id ancillaryinterrogators git-grep mainporcelain git-hash-object plumbingmanipulators -git-http-fetch synchingrepositories -git-http-push synchingrepositories -git-imap-send ancillaryinterrogators +git-http-fetch synchelpers +git-http-push synchelpers +git-imap-send foreignscminterface git-index-pack plumbingmanipulators -git-init plumbingmanipulators +git-init mainporcelain git-instaweb ancillaryinterrogators gitk mainporcelain git-local-fetch synchingrepositories git-log mainporcelain git-lost-found ancillarymanipulators git-ls-files plumbinginterrogators -git-ls-remote mainporcelain +git-ls-remote plumbinginterrogators git-ls-tree plumbinginterrogators -git-mailinfo ancillaryinterrogators -git-mailsplit ancillaryinterrogators +git-mailinfo purehelpers +git-mailsplit purehelpers git-merge-base plumbinginterrogators git-merge-file plumbingmanipulators git-merge-index plumbingmanipulators git-merge mainporcelain -git-merge-one-file ancillarymanipulators +git-merge-one-file purehelpers git-merge-tree ancillaryinterrogators git-mktag plumbingmanipulators git-mktree plumbingmanipulators @@ -126,24 +129,24 @@ git-mv mainporcelain git-name-rev plumbinginterrogators git-pack-objects plumbingmanipulators git-pack-redundant plumbinginterrogators -git-pack-refs mainporcelain -git-parse-remote ancillaryinterrogators -git-patch-id ancillaryinterrogators -git-peek-remote synchingrepositories +git-pack-refs ancillarymanipulators +git-parse-remote synchelpers +git-patch-id purehelpers +git-peek-remote purehelpers git-prune ancillarymanipulators git-prune-packed plumbingmanipulators git-pull mainporcelain git-push mainporcelain -git-quiltimport ancillarymanipulators +git-quiltimport foreignscminterface git-read-tree plumbingmanipulators git-rebase mainporcelain -git-receive-pack synchingrepositories +git-receive-pack synchelpers git-reflog ancillarymanipulators git-relink ancillarymanipulators -git-repack mainporcelain -git-repo-config plumbingmanipulators -git-request-pull ancillaryinterrogators -git-rerere mainporcelain +git-repack ancillarymanipulators +git-repo-config ancillarymanipulators +git-request-pull foreignscminterface +git-rerere ancillaryinterrogators git-reset mainporcelain git-resolve mainporcelain git-revert mainporcelain @@ -151,34 +154,33 @@ git-rev-list plumbinginterrogators git-rev-parse ancillaryinterrogators git-rm mainporcelain git-runstatus ancillaryinterrogators -git-send-email ancillaryinterrogators +git-send-email foreignscminterface git-send-pack synchingrepositories -git-shell synchingrepositories +git-shell synchelpers git-shortlog mainporcelain git-show mainporcelain -git-show-branch mainporcelain +git-show-branch ancillaryinterrogators git-show-index plumbinginterrogators git-show-ref plumbinginterrogators -git-sh-setup ancillarymanipulators +git-sh-setup purehelpers git-ssh-fetch synchingrepositories git-ssh-upload synchingrepositories git-status mainporcelain -git-stripspace ancillaryinterrogators -git-svn ancillarymanipulators -git-svnimport ancillarymanipulators -git-symbolic-ref ancillaryinterrogators -git-symbolic-ref ancillarymanipulators -git-tag ancillarymanipulators +git-stripspace purehelpers +git-svn foreignscminterface +git-svnimport foreignscminterface +git-symbolic-ref plumbingmanipulators +git-tag mainporcelain git-tar-tree plumbinginterrogators git-unpack-file plumbinginterrogators git-unpack-objects plumbingmanipulators git-update-index plumbingmanipulators -git-update-ref ancillarymanipulators +git-update-ref plumbingmanipulators git-update-server-info synchingrepositories -git-upload-archive synchingrepositories -git-upload-pack synchingrepositories +git-upload-archive synchelpers +git-upload-pack synchelpers git-var plumbinginterrogators git-verify-pack plumbinginterrogators -git-verify-tag mainporcelain -git-whatchanged mainporcelain +git-verify-tag ancillaryinterrogators +git-whatchanged ancillaryinterrogators git-write-tree plumbingmanipulators diff --git a/Documentation/git.txt b/Documentation/git.txt index ac222060ea..9761de36b3 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -93,6 +93,16 @@ Interrogators: include::cmds-ancillaryinterrogators.txt[] + +Interacting with Others +~~~~~~~~~~~~~~~~~~~~~~~ + +These commands are to interact with foreign SCM and with other +people via patch over e-mail. + +include::cmds-foreignscminterface.txt[] + + Low-level commands (plumbing) ----------------------------- @@ -102,11 +112,20 @@ development of alternative porcelains. Developers of such porcelains might start by reading about gitlink:git-update-index[1] and gitlink:git-read-tree[1]. -We divide the low-level commands into commands that manipulate objects (in +The interface (input, output, set of options and the semantics) +to these low-level commands are meant to be a lot more stable +than Porcelain level commands, because these commands are +primarily for scripted use. The interface to Porcelain commands +on the other hand are subject to change in order to improve the +end user experience. + +The following description divides +the low-level commands into commands that manipulate objects (in the repository, index, and working tree), commands that interrogate and compare objects, and commands that move objects and references between repositories. + Manipulation commands ~~~~~~~~~~~~~~~~~~~~~ @@ -127,6 +146,20 @@ Synching repositories include::cmds-synchingrepositories.txt[] +The following are helper programs used by the above; end users +typically do not use them directly. + +include::cmds-synchelpers.txt[] + + +Internal helper commands +~~~~~~~~~~~~~~~~~~~~~~~~ + +These are internal helper commands used by other commands; end +users typically do not use them directly. + +include::cmds-purehelpers.txt[] + Configuration Mechanism ----------------------- From 18bd8821cace2f830e34574df6df30e072d0ed40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Fri, 19 Jan 2007 13:43:00 +0100 Subject: [PATCH 05/63] Update documentation of fetch-pack, push and send-pack MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit add all supported options to Documentation/git-....txt and the usage strings. Signed-off-by: Uwe Kleine-König Signed-off-by: Junio C Hamano --- Documentation/git-fetch-pack.txt | 21 +++++++++++++++++---- Documentation/git-push.txt | 2 +- Documentation/git-send-pack.txt | 15 +++++++++++---- builtin-push.c | 2 +- fetch-pack.c | 2 +- send-pack.c | 4 ++-- 6 files changed, 33 insertions(+), 13 deletions(-) diff --git a/Documentation/git-fetch-pack.txt b/Documentation/git-fetch-pack.txt index 90ef127d12..bd8ebacd7d 100644 --- a/Documentation/git-fetch-pack.txt +++ b/Documentation/git-fetch-pack.txt @@ -8,7 +8,7 @@ git-fetch-pack - Receive missing objects from another repository SYNOPSIS -------- -'git-fetch-pack' [-q] [-k] [--exec=] [:] [...] +'git-fetch-pack' [--all] [--quiet|-q] [--keep|-k] [--thin] [--exec=] [--depth=] [-v] [:] [...] DESCRIPTION ----------- @@ -28,17 +28,24 @@ have a common ancestor commit. OPTIONS ------- --q:: +\--all:: + Fetch all remote refs. + +\--quiet, \-q:: Pass '-q' flag to 'git-unpack-objects'; this makes the cloning process less verbose. --k:: +\--keep, \-k:: Do not invoke 'git-unpack-objects' on received data, but create a single packfile out of it instead, and store it in the object database. If provided twice then the pack is locked against repacking. ---exec=:: +\--thin:: + Spend extra cycles to minimize the number of objects to be sent. + Use it on slower connection. + +\--exec=:: Use this to specify the path to 'git-upload-pack' on the remote side, if is not found on your $PATH. Installations of sshd ignores the user's environment @@ -50,6 +57,12 @@ OPTIONS shells by having a lean .bashrc file (they set most of the things up in .bash_profile). +\--depth=:: + Limit fetching to ancestor-chains not longer than n. + +\-v:: + Run verbosely. + :: A remote host that houses the repository. When this part is specified, 'git-upload-pack' is invoked via diff --git a/Documentation/git-push.txt b/Documentation/git-push.txt index 3e8dbcf93a..7a2e503a4e 100644 --- a/Documentation/git-push.txt +++ b/Documentation/git-push.txt @@ -8,7 +8,7 @@ git-push - Update remote refs along with associated objects SYNOPSIS -------- -'git-push' [--all] [--tags] [--exec=] [--repo=all] [-f | --force] [-v] [ ...] +'git-push' [--all] [--tags] [--exec=] [--repo=all] [-f | --force] [-v] [ ...] DESCRIPTION ----------- diff --git a/Documentation/git-send-pack.txt b/Documentation/git-send-pack.txt index eea8fe8af3..dee43a997c 100644 --- a/Documentation/git-send-pack.txt +++ b/Documentation/git-send-pack.txt @@ -8,7 +8,7 @@ git-send-pack - Push objects over git protocol to another reposiotory SYNOPSIS -------- -'git-send-pack' [--all] [--force] [--exec=] [:] [...] +'git-send-pack' [--all] [--force] [--exec=] [--verbose] [--thin] [:] [...] DESCRIPTION ----------- @@ -21,23 +21,30 @@ updates it from the current repository, sending named refs. OPTIONS ------- ---exec=:: +\--exec=:: Path to the 'git-receive-pack' program on the remote end. Sometimes useful when pushing to a remote repository over ssh, and you do not have the program in a directory on the default $PATH. ---all:: +\--all:: Instead of explicitly specifying which refs to update, update all refs that locally exist. ---force:: +\--force:: Usually, the command refuses to update a remote ref that is not an ancestor of the local ref used to overwrite it. This flag disables the check. What this means is that the remote repository can lose commits; use it with care. +\--verbose:: + Run verbosely. + +\--thin:: + Spend extra cycles to minimize the number of objects to be sent. + Use it on slower connection. + :: A remote host to house the repository. When this part is specified, 'git-receive-pack' is invoked via diff --git a/builtin-push.c b/builtin-push.c index 7a3d2bb064..ba7981f5bf 100644 --- a/builtin-push.c +++ b/builtin-push.c @@ -8,7 +8,7 @@ #define MAX_URI (16) -static const char push_usage[] = "git-push [--all] [--tags] [-f | --force] [...]"; +static const char push_usage[] = "git-push [--all] [--tags] [--exec=] [--repo=all] [-f | --force] [-v] [ ...]"; static int all, tags, force, thin = 1, verbose; static const char *execute; diff --git a/fetch-pack.c b/fetch-pack.c index 1530a94794..726140a1a5 100644 --- a/fetch-pack.c +++ b/fetch-pack.c @@ -12,7 +12,7 @@ static int verbose; static int fetch_all; static int depth; static const char fetch_pack_usage[] = -"git-fetch-pack [--all] [-q] [-v] [-k] [--thin] [--exec=upload-pack] [--depth=] [host:]directory ..."; +"git-fetch-pack [--all] [--quiet|-q] [--keep|-k] [--thin] [--exec=] [--depth=] [-v] [:] [...]"; static const char *exec = "git-upload-pack"; #define COMPLETE (1U << 0) diff --git a/send-pack.c b/send-pack.c index 6756264b29..ec2c1089b2 100644 --- a/send-pack.c +++ b/send-pack.c @@ -6,8 +6,8 @@ #include "exec_cmd.h" static const char send_pack_usage[] = -"git-send-pack [--all] [--exec=git-receive-pack] [...]\n" -" --all and explicit specification are mutually exclusive."; +"git-send-pack [--all] [--force] [--exec=] [--verbose] [--thin] [:] [...]\n" +" --all and explicit specification are mutually exclusive."; static const char *exec = "git-receive-pack"; static int verbose; static int send_all; From 060aafc11fb6140f1b33326f03e70dad5762a624 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Fri, 19 Jan 2007 13:46:16 +0100 Subject: [PATCH 06/63] make --exec=... option to git-push configurable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Having to specify git push --exec=... is annoying if you cannot have git-receivepack in your PATH on the remote side (or don't want to). This introduces the config item remote..receivepack to override the default value (which is "git-receive-pack"). Signed-off-by: Uwe Kleine-König Signed-off-by: Junio C Hamano --- Documentation/config.txt | 4 ++++ builtin-push.c | 11 +++++++++++ 2 files changed, 15 insertions(+) diff --git a/Documentation/config.txt b/Documentation/config.txt index da7fde56b4..f1f409d24b 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -356,6 +356,10 @@ remote..push:: The default set of "refspec" for gitlink:git-push[1]. See gitlink:git-push[1]. +remote..receivepack:: + The default program to execute on the remote side when pulling. See + option \--exec of gitlink:git-push[1]. + repack.usedeltabaseoffset:: Allow gitlink:git-repack[1] to create packs that uses delta-base offset. Defaults to false. diff --git a/builtin-push.c b/builtin-push.c index ba7981f5bf..6b3c03b8a6 100644 --- a/builtin-push.c +++ b/builtin-push.c @@ -143,6 +143,7 @@ static const char *config_repo; static int config_repo_len; static int config_current_uri; static int config_get_refspecs; +static int config_get_receivepack; static int get_remote_config(const char* key, const char* value) { @@ -157,6 +158,15 @@ static int get_remote_config(const char* key, const char* value) else if (config_get_refspecs && !strcmp(key + 7 + config_repo_len, ".push")) add_refspec(xstrdup(value)); + else if (config_get_receivepack && + !strcmp(key + 7 + config_repo_len, ".receivepack")) { + if (!execute) { + char *ex = xmalloc(strlen(value) + 8); + sprintf(ex, "--exec=%s", value); + execute = ex; + } else + error("more than one receivepack given, using the first"); + } } return 0; } @@ -168,6 +178,7 @@ static int get_config_remotes_uri(const char *repo, const char *uri[MAX_URI]) config_current_uri = 0; config_uri = uri; config_get_refspecs = !(refspec_nr || all || tags); + config_get_receivepack = (execute == NULL); git_config(get_remote_config); return config_current_uri; From d23842fd53e61f32c189a6ec902c4133abf29878 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Fri, 19 Jan 2007 13:49:27 +0100 Subject: [PATCH 07/63] rename --exec to --receive-pack for push and send-pack MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For now it's just to get a more descriptive name. Later we might update the push protocol to run more than one program on the other end. Moreover this matches better the corresponding config option remote.. receivepack. --exec continues to work Signed-off-by: Uwe Kleine-König Signed-off-by: Junio C Hamano --- Documentation/git-push.txt | 7 +++++-- Documentation/git-send-pack.txt | 7 +++++-- builtin-push.c | 24 ++++++++++++++---------- send-pack.c | 12 ++++++++---- 4 files changed, 32 insertions(+), 18 deletions(-) diff --git a/Documentation/git-push.txt b/Documentation/git-push.txt index 7a2e503a4e..f8cc2b5432 100644 --- a/Documentation/git-push.txt +++ b/Documentation/git-push.txt @@ -8,7 +8,7 @@ git-push - Update remote refs along with associated objects SYNOPSIS -------- -'git-push' [--all] [--tags] [--exec=] [--repo=all] [-f | --force] [-v] [ ...] +'git-push' [--all] [--tags] [--receive-pack=] [--repo=all] [-f | --force] [-v] [ ...] DESCRIPTION ----------- @@ -67,12 +67,15 @@ the remote repository. addition to refspecs explicitly listed on the command line. -\--exec:: +\--receive-pack=:: Path to the 'git-receive-pack' program on the remote end. Sometimes useful when pushing to a remote repository over ssh, and you do not have the program in a directory on the default $PATH. +\--exec=:: + Same as \--receive-pack=. + -f, \--force:: Usually, the command refuses to update a remote ref that is not a descendant of the local ref used to overwrite it. diff --git a/Documentation/git-send-pack.txt b/Documentation/git-send-pack.txt index dee43a997c..2f6267ce60 100644 --- a/Documentation/git-send-pack.txt +++ b/Documentation/git-send-pack.txt @@ -8,7 +8,7 @@ git-send-pack - Push objects over git protocol to another reposiotory SYNOPSIS -------- -'git-send-pack' [--all] [--force] [--exec=] [--verbose] [--thin] [:] [...] +'git-send-pack' [--all] [--force] [--receive-pack=] [--verbose] [--thin] [:] [...] DESCRIPTION ----------- @@ -21,12 +21,15 @@ updates it from the current repository, sending named refs. OPTIONS ------- -\--exec=:: +\--receive-pack=:: Path to the 'git-receive-pack' program on the remote end. Sometimes useful when pushing to a remote repository over ssh, and you do not have the program in a directory on the default $PATH. +\--exec=:: + Same as \--receive-pack=. + \--all:: Instead of explicitly specifying which refs to update, update all refs that locally exist. diff --git a/builtin-push.c b/builtin-push.c index 6b3c03b8a6..5f4d7d34d3 100644 --- a/builtin-push.c +++ b/builtin-push.c @@ -8,10 +8,10 @@ #define MAX_URI (16) -static const char push_usage[] = "git-push [--all] [--tags] [--exec=] [--repo=all] [-f | --force] [-v] [ ...]"; +static const char push_usage[] = "git-push [--all] [--tags] [--receive-pack=] [--repo=all] [-f | --force] [-v] [ ...]"; static int all, tags, force, thin = 1, verbose; -static const char *execute; +static const char *receivepack; #define BUF_SIZE (2084) static char buffer[BUF_SIZE]; @@ -160,10 +160,10 @@ static int get_remote_config(const char* key, const char* value) add_refspec(xstrdup(value)); else if (config_get_receivepack && !strcmp(key + 7 + config_repo_len, ".receivepack")) { - if (!execute) { - char *ex = xmalloc(strlen(value) + 8); - sprintf(ex, "--exec=%s", value); - execute = ex; + if (!receivepack) { + char *rp = xmalloc(strlen(value) + 16); + sprintf(rp, "--receive-pack=%s", value); + receivepack = rp; } else error("more than one receivepack given, using the first"); } @@ -178,7 +178,7 @@ static int get_config_remotes_uri(const char *repo, const char *uri[MAX_URI]) config_current_uri = 0; config_uri = uri; config_get_refspecs = !(refspec_nr || all || tags); - config_get_receivepack = (execute == NULL); + config_get_receivepack = (receivepack == NULL); git_config(get_remote_config); return config_current_uri; @@ -263,8 +263,8 @@ static int do_push(const char *repo) argv[argc++] = "--all"; if (force) argv[argc++] = "--force"; - if (execute) - argv[argc++] = execute; + if (receivepack) + argv[argc++] = receivepack; common_argc = argc; for (i = 0; i < n; i++) { @@ -347,8 +347,12 @@ int cmd_push(int argc, const char **argv, const char *prefix) thin = 0; continue; } + if (!strncmp(arg, "--receive-pack=", 15)) { + receivepack = arg; + continue; + } if (!strncmp(arg, "--exec=", 7)) { - execute = arg; + receivepack = arg; continue; } usage(push_usage); diff --git a/send-pack.c b/send-pack.c index ec2c1089b2..cd478dd82f 100644 --- a/send-pack.c +++ b/send-pack.c @@ -6,9 +6,9 @@ #include "exec_cmd.h" static const char send_pack_usage[] = -"git-send-pack [--all] [--force] [--exec=] [--verbose] [--thin] [:] [...]\n" +"git-send-pack [--all] [--force] [--receive-pack=] [--verbose] [--thin] [:] [...]\n" " --all and explicit specification are mutually exclusive."; -static const char *exec = "git-receive-pack"; +static const char *receivepack = "git-receive-pack"; static int verbose; static int send_all; static int force_update; @@ -377,8 +377,12 @@ int main(int argc, char **argv) char *arg = *argv; if (*arg == '-') { + if (!strncmp(arg, "--receive-pack=", 15)) { + receivepack = arg + 15; + continue; + } if (!strncmp(arg, "--exec=", 7)) { - exec = arg + 7; + receivepack = arg + 7; continue; } if (!strcmp(arg, "--all")) { @@ -413,7 +417,7 @@ int main(int argc, char **argv) usage(send_pack_usage); verify_remote_names(nr_heads, heads); - pid = git_connect(fd, dest, exec); + pid = git_connect(fd, dest, receivepack); if (pid < 0) return 1; ret = send_pack(fd[0], fd[1], nr_heads, heads); From 6f71686e0bac2337cdaf9057893a16a47e7d1033 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sat, 20 Jan 2007 02:25:37 +0100 Subject: [PATCH 08/63] config_set_multivar(): disallow newlines in keys This will no longer work: $ git repo-config 'key.with newline' some-value Signed-off-by: Johannes Schindelin --- config.c | 5 +++++ t/t1300-repo-config.sh | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/config.c b/config.c index b6082f597c..c08c66890f 100644 --- a/config.c +++ b/config.c @@ -661,6 +661,11 @@ int git_config_set_multivar(const char* key, const char* value, goto out_free; } c = tolower(c); + } else if (c == '\n') { + fprintf(stderr, "invalid key (newline): %s\n", key); + free(store.key); + ret = 1; + goto out_free; } store.key[i] = c; } diff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh index 60acdd368b..eb7455ba21 100755 --- a/t/t1300-repo-config.sh +++ b/t/t1300-repo-config.sh @@ -418,5 +418,11 @@ EOF test_expect_success 'quoting' 'cmp .git/config expect' +test_expect_failure 'key with newline' 'git repo-config key.with\\\ +newline 123' + +test_expect_success 'value with newline' 'git repo-config key.sub value.with\\\ +newline' + test_done From e86eb6668ee0c574ba0b6c2c94564d4159a6fcc7 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 19 Jan 2007 01:15:15 -0800 Subject: [PATCH 09/63] dwim_ref(): Separate name-to-ref DWIM code out. I'll be using this in another function to figure out what to pass to resolve_ref(). Signed-off-by: Junio C Hamano --- cache.h | 2 ++ sha1_name.c | 38 ++++++++++++++++++++++++-------------- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/cache.h b/cache.h index e6e19bdef3..a218db3b30 100644 --- a/cache.h +++ b/cache.h @@ -299,6 +299,8 @@ extern int get_sha1_hex(const char *hex, unsigned char *sha1); extern char *sha1_to_hex(const unsigned char *sha1); /* static buffer result! */ extern int read_ref(const char *filename, unsigned char *sha1); extern const char *resolve_ref(const char *path, unsigned char *sha1, int, int *); +extern int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref); + extern int create_symref(const char *ref, const char *refs_heads_master); extern int validate_headref(const char *ref); diff --git a/sha1_name.c b/sha1_name.c index 6d7cd78381..ae31851900 100644 --- a/sha1_name.c +++ b/sha1_name.c @@ -235,7 +235,7 @@ static int ambiguous_path(const char *path, int len) return slash; } -static int get_sha1_basic(const char *str, int len, unsigned char *sha1) +int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref) { static const char *fmt[] = { "%.*s", @@ -246,13 +246,32 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1) "refs/remotes/%.*s/HEAD", NULL }; + const char **p, *r; + int refs_found = 0; + + *ref = NULL; + for (p = fmt; *p; p++) { + unsigned char sha1_from_ref[20]; + unsigned char *this_result; + + this_result = refs_found ? sha1_from_ref : sha1; + r = resolve_ref(mkpath(*p, len, str), this_result, 1, NULL); + if (r) { + if (!refs_found++) + *ref = xstrdup(r); + if (!warn_ambiguous_refs) + break; + } + } + return refs_found; +} + +static int get_sha1_basic(const char *str, int len, unsigned char *sha1) +{ static const char *warning = "warning: refname '%.*s' is ambiguous.\n"; - const char **p, *ref; char *real_ref = NULL; int refs_found = 0; int at, reflog_len; - unsigned char *this_result; - unsigned char sha1_from_ref[20]; if (len == 40 && !get_sha1_hex(str, sha1)) return 0; @@ -273,16 +292,7 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1) if (ambiguous_path(str, len)) return -1; - for (p = fmt; *p; p++) { - this_result = refs_found ? sha1_from_ref : sha1; - ref = resolve_ref(mkpath(*p, len, str), this_result, 1, NULL); - if (ref) { - if (!refs_found++) - real_ref = xstrdup(ref); - if (!warn_ambiguous_refs) - break; - } - } + refs_found = dwim_ref(str, len, sha1, &real_ref); if (!refs_found) return -1; From 16d7cc90dd6e68a170362ae6f0fbfbc504fd246b Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 19 Jan 2007 01:19:05 -0800 Subject: [PATCH 10/63] Extend read_ref_at() to be usable from places other than sha1_name. You can pass an extra argument to the function to receive the reflog message information. Also when the log does not go back beyond the point the user asked, the cut-off time and count are given back to the caller for emitting the error messages as appropriately. We could later add configuration for get_sha1_basic() to make it an error instead of it being just a warning. Signed-off-by: Junio C Hamano --- refs.c | 41 +++++++++++++++++++++++++++++++++-------- refs.h | 2 +- sha1_name.c | 16 +++++++++++++++- t/t1400-update-ref.sh | 6 +++--- 4 files changed, 52 insertions(+), 13 deletions(-) diff --git a/refs.c b/refs.c index 6310cf46e0..60375a786f 100644 --- a/refs.c +++ b/refs.c @@ -1017,7 +1017,21 @@ int write_ref_sha1(struct ref_lock *lock, return 0; } -int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char *sha1) +static char *ref_msg(const char *line, const char *endp) +{ + const char *ep; + char *msg; + + line += 82; + for (ep = line; ep < endp && *ep != '\n'; ep++) + ; + msg = xmalloc(ep - line + 1); + memcpy(msg, line, ep - line); + msg[ep - line] = 0; + return msg; +} + +int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char *sha1, char **msg, unsigned long *cutoff_time, int *cutoff_tz, int *cutoff_cnt) { const char *logfile, *logdata, *logend, *rec, *lastgt, *lastrec; char *tz_c; @@ -1054,6 +1068,14 @@ int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char * die("Log %s is corrupt.", logfile); date = strtoul(lastgt + 1, &tz_c, 10); if (date <= at_time || cnt == 0) { + if (msg) + *msg = ref_msg(rec, logend); + if (cutoff_time) + *cutoff_time = date; + if (cutoff_tz) + *cutoff_tz = tz; + if (cutoff_cnt) + *cutoff_cnt = reccnt; if (lastrec) { if (get_sha1_hex(lastrec, logged_sha1)) die("Log %s is corrupt.", logfile); @@ -1097,14 +1119,17 @@ int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char * tz = strtoul(tz_c, NULL, 10); if (get_sha1_hex(logdata, sha1)) die("Log %s is corrupt.", logfile); + if (msg) + *msg = ref_msg(logdata, logend); munmap(log_mapped, st.st_size); - if (at_time) - fprintf(stderr, "warning: Log %s only goes back to %s.\n", - logfile, show_rfc2822_date(date, tz)); - else - fprintf(stderr, "warning: Log %s only has %d entries.\n", - logfile, reccnt); - return 0; + + if (cutoff_time) + *cutoff_time = date; + if (cutoff_tz) + *cutoff_tz = tz; + if (cutoff_cnt) + *cutoff_cnt = reccnt; + return 1; } int for_each_reflog_ent(const char *ref, each_reflog_ent_fn fn, void *cb_data) diff --git a/refs.h b/refs.h index 0e877e82ee..33450f13e7 100644 --- a/refs.h +++ b/refs.h @@ -42,7 +42,7 @@ extern void unlock_ref(struct ref_lock *lock); extern int write_ref_sha1(struct ref_lock *lock, const unsigned char *sha1, const char *msg); /** Reads log for the value of ref during at_time. **/ -extern int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char *sha1); +extern int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char *sha1, char **msg, unsigned long *cutoff_time, int *cutoff_tz, int *cutoff_cnt); /* iterate over reflog entries */ typedef int each_reflog_ent_fn(unsigned char *osha1, unsigned char *nsha1, const char *, unsigned long, int, const char *, void *); diff --git a/sha1_name.c b/sha1_name.c index ae31851900..9dfb3ac574 100644 --- a/sha1_name.c +++ b/sha1_name.c @@ -304,6 +304,9 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1) /* Is it asking for N-th entry, or approxidate? */ int nth, i; unsigned long at_time; + unsigned long co_time; + int co_tz, co_cnt; + for (i = nth = 0; 0 <= nth && i < reflog_len; i++) { char ch = str[at+2+i]; if ('0' <= ch && ch <= '9') @@ -315,7 +318,18 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1) at_time = 0; else at_time = approxidate(str + at + 2); - read_ref_at(real_ref, at_time, nth, sha1); + if (read_ref_at(real_ref, at_time, nth, sha1, NULL, + &co_time, &co_tz, &co_cnt)) { + if (at_time) + fprintf(stderr, + "warning: Log for '%.*s' only goes " + "back to %s.\n", len, str, + show_rfc2822_date(co_time, co_tz)); + else + fprintf(stderr, + "warning: Log for '%.*s' only has " + "%d entries.\n", len, str, co_cnt); + } } free(real_ref); diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh index 5637cb5eac..e48e2b7189 100755 --- a/t/t1400-update-ref.sh +++ b/t/t1400-update-ref.sh @@ -138,19 +138,19 @@ test_expect_success \ 'rm -f o e git-rev-parse --verify "master@{May 25 2005}" >o 2>e && test '"$C"' = $(cat o) && - test "warning: Log .git/logs/'"$m only goes back to $ed"'." = "$(cat e)"' + test "warning: Log for '\'master\'' only goes back to $ed." = "$(cat e)"' test_expect_success \ "Query master@{2005-05-25} (before history)" \ 'rm -f o e git-rev-parse --verify master@{2005-05-25} >o 2>e && test '"$C"' = $(cat o) && - echo test "warning: Log .git/logs/'"$m only goes back to $ed"'." = "$(cat e)"' + echo test "warning: Log for '\'master\'' only goes back to $ed." = "$(cat e)"' test_expect_success \ 'Query "master@{May 26 2005 23:31:59}" (1 second before history)' \ 'rm -f o e git-rev-parse --verify "master@{May 26 2005 23:31:59}" >o 2>e && test '"$C"' = $(cat o) && - test "warning: Log .git/logs/'"$m only goes back to $ed"'." = "$(cat e)"' + test "warning: Log for '\''master'\'' only goes back to $ed." = "$(cat e)"' test_expect_success \ 'Query "master@{May 26 2005 23:32:00}" (exactly history start)' \ 'rm -f o e From 76a44c5c0b25b1dbde56d17353d5b0821b7c6981 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 19 Jan 2007 01:20:23 -0800 Subject: [PATCH 11/63] show-branch --reflog: show the reflog message at the top. This changes the output so the list at the top shows the reflog message, along with their relative timestamps. You can use --reflog= to show most recent log entries, or use --reflog=, to show entries going back from the entry . can be either a number (so --reflog=4,20 shows 4 records starting from @{20}) or a timestamp (e.g. --reflog='4,1 day'). Here is a sample output (with --list option): $ git show-branch --reflog=10 --list jc/show-reflog [jc/show-reflog@{0}] (3 minutes ago) commit (amend): show-branch --ref [jc/show-reflog@{1}] (5 minutes ago) reset HEAD^ [jc/show-reflog@{2}] (14 minutes ago) commit: show-branch --reflog: sho [jc/show-reflog@{3}] (14 minutes ago) commit: show-branch --reflog: sho [jc/show-reflog@{4}] (18 minutes ago) commit (amend): Extend read_ref_a [jc/show-reflog@{5}] (18 minutes ago) commit (amend): Extend read_ref_a [jc/show-reflog@{6}] (18 minutes ago) commit (amend): Extend read_ref_a [jc/show-reflog@{7}] (18 minutes ago) am: read_ref_at(): allow retrievi [jc/show-reflog@{8}] (18 minutes ago) reset --hard HEAD~4 [jc/show-reflog@{9}] (61 minutes ago) commit: show-branch --reflog: use This shows what I did more cleanly: $ git show-branch --reflog=10 jc/show-reflog ! [jc/show-reflog@{0}] (3 minutes ago) commit (amend): show-branch --ref ! [jc/show-reflog@{1}] (5 minutes ago) reset HEAD^ ! [jc/show-reflog@{2}] (14 minutes ago) commit: show-branch --reflog: ! [jc/show-reflog@{3}] (14 minutes ago) commit: show-branch --reflog: ! [jc/show-reflog@{4}] (18 minutes ago) commit (amend): Extend read_ ! [jc/show-reflog@{5}] (18 minutes ago) commit (amend): Extend read ! [jc/show-reflog@{6}] (18 minutes ago) commit (amend): Extend rea ! [jc/show-reflog@{7}] (18 minutes ago) am: read_ref_at(): allow ! [jc/show-reflog@{8}] (18 minutes ago) reset --hard HEAD~4 ! [jc/show-reflog@{9}] (61 minutes ago) commit: show-branch --r ---------- + [jc/show-reflog@{0}] show-branch --reflog: show the reflog + [jc/show-reflog@{2}] show-branch --reflog: show the reflog +++ [jc/show-reflog@{1}] show-branch --reflog: show the reflog +++++ [jc/show-reflog@{4}] Extend read_ref_at() to be usable fro + [jc/show-reflog@{5}] Extend read_ref_at() to be usable fro + [jc/show-reflog@{6}] Extend read_ref_at() to be usable fro + [jc/show-reflog@{7}] read_ref_at(): allow retrieving the r + [jc/show-reflog@{9}] show-branch --reflog: use updated rea + [jc/show-reflog@{9}^] read_ref_at(): allow reporting the c + [jc/show-reflog@{9}~2] show-branch --reflog: show the refl + [jc/show-reflog@{9}~3] read_ref_at(): allow retrieving the ++++++++++ [jc/show-reflog@{8}] dwim_ref(): Separate name-to-ref DWIM At @{9}, I had a commit to complete 5 patch series, but I wanted to consolidate two commits that enhances read_ref_at() into one (they were @{9}^ and @{9}~3), and another two that touch show-branch into one (@{9} and @{9}~2). I first saved them with "format-patch -4", and then did a reset at @{8}. At @{7}, I applied one of them with "am", and then used "git-apply" on the other one, and amended the commit at @{6} (so @{6} and @{7} has the same parent). I did not like the log message, so I amended again at @{5}. Then I cherry-picked @{9}~2 to create @{3} (the log message shows that it needs to learn to set GIT_REFLOG_ACTION -- it uses "git-commit" and the log entry is attributed for it). Another cherry-pick built @{2} out of @{9}, but what I wanted to do was to squash these two into one, so I did a "reset HEAD^" at @{1} and then made the final commit by amending what was at the top. Signed-off-by: Junio C Hamano --- Documentation/git-show-branch.txt | 15 +++- builtin-show-branch.c | 122 +++++++++++++++++++++++------- refs.c | 5 +- 3 files changed, 109 insertions(+), 33 deletions(-) diff --git a/Documentation/git-show-branch.txt b/Documentation/git-show-branch.txt index 912e15bcba..529f3a648a 100644 --- a/Documentation/git-show-branch.txt +++ b/Documentation/git-show-branch.txt @@ -11,7 +11,7 @@ SYNOPSIS 'git-show-branch' [--all] [--remotes] [--topo-order] [--current] [--more= | --list | --independent | --merge-base] [--no-name | --sha1-name] [--topics] [ | ]... -'git-show-branch' --reflog[=] +'git-show-branch' --reflog[=[,]] [--list] DESCRIPTION ----------- @@ -97,8 +97,10 @@ OPTIONS will show the revisions given by "git rev-list {caret}master topic1 topic2" ---reflog[=] :: - Shows most recent ref-log entries for the given ref. +--reflog[=[,]] :: + Shows most recent ref-log entries for the given + ref. If is given, entries going back from + that entry. can be specified as count or date Note that --more, --list, --independent and --merge-base options @@ -165,6 +167,13 @@ With this, `git show-branch` without extra parameters would show only the primary branches. In addition, if you happen to be on your topic branch, it is shown as well. +------------ +$ git show-branch --reflog='10,1 hour ago' --list master +------------ + +shows 10 reflog entries going back from the tip as of 1 hour ago. +Without `--list`, the output also shows how these tips are +topologically related with each other. Author diff --git a/builtin-show-branch.c b/builtin-show-branch.c index c67f2fa2fe..452c15fd1b 100644 --- a/builtin-show-branch.c +++ b/builtin-show-branch.c @@ -346,18 +346,21 @@ static void sort_ref_range(int bottom, int top) compare_ref_name); } -static int append_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data) +static int append_ref(const char *refname, const unsigned char *sha1, + int allow_dups) { struct commit *commit = lookup_commit_reference_gently(sha1, 1); int i; if (!commit) return 0; - /* Avoid adding the same thing twice */ - for (i = 0; i < ref_name_cnt; i++) - if (!strcmp(refname, ref_name[i])) - return 0; + if (!allow_dups) { + /* Avoid adding the same thing twice */ + for (i = 0; i < ref_name_cnt; i++) + if (!strcmp(refname, ref_name[i])) + return 0; + } if (MAX_REVS <= ref_name_cnt) { fprintf(stderr, "warning: ignoring %s; " "cannot handle more than %d refs\n", @@ -380,7 +383,7 @@ static int append_head_ref(const char *refname, const unsigned char *sha1, int f */ if (get_sha1(refname + ofs, tmp) || hashcmp(tmp, sha1)) ofs = 5; - return append_ref(refname + ofs, sha1, flag, cb_data); + return append_ref(refname + ofs, sha1, 0); } static int append_remote_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data) @@ -394,14 +397,14 @@ static int append_remote_ref(const char *refname, const unsigned char *sha1, int */ if (get_sha1(refname + ofs, tmp) || hashcmp(tmp, sha1)) ofs = 5; - return append_ref(refname + ofs, sha1, flag, cb_data); + return append_ref(refname + ofs, sha1, 0); } static int append_tag_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data) { if (strncmp(refname, "refs/tags/", 10)) return 0; - return append_ref(refname + 5, sha1, flag, cb_data); + return append_ref(refname + 5, sha1, 0); } static const char *match_ref_pattern = NULL; @@ -434,7 +437,7 @@ static int append_matching_ref(const char *refname, const unsigned char *sha1, i return append_head_ref(refname, sha1, flag, cb_data); if (!strncmp("refs/tags/", refname, 10)) return append_tag_ref(refname, sha1, flag, cb_data); - return append_ref(refname, sha1, flag, cb_data); + return append_ref(refname, sha1, 0); } static void snarf_refs(int head, int remotes) @@ -507,7 +510,7 @@ static void append_one_rev(const char *av) { unsigned char revkey[20]; if (!get_sha1(av, revkey)) { - append_ref(av, revkey, 0, NULL); + append_ref(av, revkey, 0); return; } if (strchr(av, '*') || strchr(av, '?') || strchr(av, '[')) { @@ -562,9 +565,24 @@ static int omit_in_dense(struct commit *commit, struct commit **rev, int n) return 0; } +static void parse_reflog_param(const char *arg, int *cnt, const char **base) +{ + char *ep; + *cnt = strtoul(arg, &ep, 10); + if (*ep == ',') + *base = ep + 1; + else if (*ep) + die("unrecognized reflog param '%s'", arg + 9); + else + *base = NULL; + if (*cnt <= 0) + *cnt = DEFAULT_REFLOG; +} + int cmd_show_branch(int ac, const char **av, const char *prefix) { struct commit *rev[MAX_REVS], *commit; + char *reflog_msg[MAX_REVS]; struct commit_list *list = NULL, *seen = NULL; unsigned int rev_mask[MAX_REVS]; int num_rev, i, extra = 0; @@ -585,6 +603,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) int topics = 0; int dense = 1; int reflog = 0; + const char *reflog_base = NULL; git_config(git_show_branch_config); @@ -631,21 +650,27 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) else if (!strcmp(arg, "--reflog")) { reflog = DEFAULT_REFLOG; } - else if (!strncmp(arg, "--reflog=", 9)) { - char *end; - reflog = strtoul(arg + 9, &end, 10); - if (*end != '\0') - die("unrecognized reflog count '%s'", arg + 9); - } + else if (!strncmp(arg, "--reflog=", 9)) + parse_reflog_param(arg + 9, &reflog, &reflog_base); else usage(show_branch_usage); ac--; av++; } ac--; av++; - /* Only one of these is allowed */ - if (1 < independent + merge_base + (extra != 0) + (!!reflog)) - usage(show_branch_usage); + if (!!extra || !!reflog) { + /* "listing" mode is incompatible with + * independent nor merge-base modes. + */ + if (independent || merge_base) + usage(show_branch_usage); + if (!!reflog && (0 < extra)) + /* + * Asking for --more in reflog mode does not + * make sense. + */ + usage(show_branch_usage); + } /* If nothing is specified, show all branches by default */ if (ac + all_heads + all_remotes == 0) @@ -654,14 +679,51 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) if (all_heads + all_remotes) snarf_refs(all_heads, all_remotes); if (reflog) { - int reflen; - if (!ac) + unsigned char sha1[20]; + char nth_desc[256]; + char *ref; + int base = 0; + if (ac != 1) die("--reflog option needs one branch name"); - reflen = strlen(*av); + if (!dwim_ref(*av, strlen(*av), sha1, &ref)) + die("No such ref %s", *av); + + /* Has the base been specified? */ + if (reflog_base) { + char *ep; + base = strtoul(reflog_base, &ep, 10); + if (*ep) { + /* Ah, that is a date spec... */ + unsigned long at; + at = approxidate(reflog_base); + read_ref_at(ref, at, -1, sha1, NULL, + NULL, NULL, &base); + } + } + for (i = 0; i < reflog; i++) { - char *name = xmalloc(reflen + 20); - sprintf(name, "%s@{%d}", *av, i); - append_one_rev(name); + char *logmsg, *msg, *m; + unsigned long timestamp; + int tz; + + if (read_ref_at(ref, 0, base+i, sha1, &logmsg, + ×tamp, &tz, NULL)) { + reflog = i; + break; + } + msg = strchr(logmsg, '\t'); + if (!msg) + msg = "(none)"; + else + msg++; + m = xmalloc(strlen(msg) + 200); + sprintf(m, "(%s) %s", + show_date(timestamp, 0, 1), + msg); + reflog_msg[i] = m; + free(logmsg); + sprintf(nth_desc, "%s@{%d}", *av, base+i); + append_ref(nth_desc, sha1, 1); } } else { @@ -760,8 +822,14 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) printf("%c [%s] ", is_head ? '*' : '!', ref_name[i]); } - /* header lines never need name */ - show_one_commit(rev[i], 1); + + if (!reflog) { + /* header lines never need name */ + show_one_commit(rev[i], 1); + } + else + puts(reflog_msg[i]); + if (is_head) head_at = i; } diff --git a/refs.c b/refs.c index 60375a786f..81173282fc 100644 --- a/refs.c +++ b/refs.c @@ -1068,6 +1068,7 @@ int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char * die("Log %s is corrupt.", logfile); date = strtoul(lastgt + 1, &tz_c, 10); if (date <= at_time || cnt == 0) { + tz = strtoul(tz_c, NULL, 10); if (msg) *msg = ref_msg(rec, logend); if (cutoff_time) @@ -1075,14 +1076,13 @@ int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char * if (cutoff_tz) *cutoff_tz = tz; if (cutoff_cnt) - *cutoff_cnt = reccnt; + *cutoff_cnt = reccnt - 1; if (lastrec) { if (get_sha1_hex(lastrec, logged_sha1)) die("Log %s is corrupt.", logfile); if (get_sha1_hex(rec + 41, sha1)) die("Log %s is corrupt.", logfile); if (hashcmp(logged_sha1, sha1)) { - tz = strtoul(tz_c, NULL, 10); fprintf(stderr, "warning: Log %s has gap after %s.\n", logfile, show_rfc2822_date(date, tz)); @@ -1096,7 +1096,6 @@ int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char * if (get_sha1_hex(rec + 41, logged_sha1)) die("Log %s is corrupt.", logfile); if (hashcmp(logged_sha1, sha1)) { - tz = strtoul(tz_c, NULL, 10); fprintf(stderr, "warning: Log %s unexpectedly ended on %s.\n", logfile, show_rfc2822_date(date, tz)); From b15af07928cf8aaf405407ec94fca4a9202edbc2 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 19 Jan 2007 22:51:49 -0800 Subject: [PATCH 12/63] show-branch --reflog: tighten input validation. Signed-off-by: Junio C Hamano --- builtin-show-branch.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/builtin-show-branch.c b/builtin-show-branch.c index 452c15fd1b..651d27c2c0 100644 --- a/builtin-show-branch.c +++ b/builtin-show-branch.c @@ -4,7 +4,9 @@ #include "builtin.h" static const char show_branch_usage[] = -"git-show-branch [--sparse] [--current] [--all] [--remotes] [--topo-order] [--more=count | --list | --independent | --merge-base ] [--topics] [...] | --reflog[=n] "; +"git-show-branch [--sparse] [--current] [--all] [--remotes] [--topo-order] [--more=count | --list | --independent | --merge-base ] [--topics] [...] | --reflog[=n[,b]] "; +static const char show_branch_usage_reflog[] = +"--reflog is incompatible with --all, --remotes, --independent or --merge-base"; static int default_num; static int default_alloc; @@ -664,12 +666,14 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) */ if (independent || merge_base) usage(show_branch_usage); - if (!!reflog && (0 < extra)) + if (!!reflog && ((0 < extra) || all_heads || all_remotes)) /* * Asking for --more in reflog mode does not - * make sense. + * make sense. --list is Ok. + * + * Also --all and --remotes do not make sense either. */ - usage(show_branch_usage); + usage(show_branch_usage_reflog); } /* If nothing is specified, show all branches by default */ @@ -685,6 +689,9 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) int base = 0; if (ac != 1) die("--reflog option needs one branch name"); + if (MAX_REVS < reflog) + die("Only %d entries can be shown at one time.", + MAX_REVS); if (!dwim_ref(*av, strlen(*av), sha1, &ref)) die("No such ref %s", *av); From da8f070cee6795594e4ac2af9f1e11cf9a7d3649 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sat, 20 Jan 2007 22:21:38 +0100 Subject: [PATCH 13/63] show_date(): fix relative dates We pass a timestamp (i.e. number of seconds elapsed since Jan 1 1970, 00:00:00 GMT) to the function. So there is no need to "fix" the timestamp according to the timezone. Signed-off-by: Johannes Schindelin --- date.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/date.c b/date.c index 7acb8cbd91..542c004c2e 100644 --- a/date.c +++ b/date.c @@ -62,12 +62,11 @@ const char *show_date(unsigned long time, int tz, int relative) if (relative) { unsigned long diff; - time_t t = gm_time_t(time, tz); struct timeval now; gettimeofday(&now, NULL); - if (now.tv_sec < t) + if (now.tv_sec < time) return "in the future"; - diff = now.tv_sec - t; + diff = now.tv_sec - time; if (diff < 90) { snprintf(timebuf, sizeof(timebuf), "%lu seconds ago", diff); return timebuf; From 16bfefeebcac375ff51652eb8b866b7b65cb4905 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 20 Jan 2007 18:57:06 -0800 Subject: [PATCH 14/63] show-branch --reflog: fix show_date() call Not passing tz to show_date() is not a fix. Signed-off-by: Junio C Hamano --- builtin-show-branch.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin-show-branch.c b/builtin-show-branch.c index 651d27c2c0..b54c410e14 100644 --- a/builtin-show-branch.c +++ b/builtin-show-branch.c @@ -725,7 +725,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) msg++; m = xmalloc(strlen(msg) + 200); sprintf(m, "(%s) %s", - show_date(timestamp, 0, 1), + show_date(timestamp, tz, 1), msg); reflog_msg[i] = m; free(logmsg); From cd554bb1733feb98ecee647176d0328cab53c2b7 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sun, 21 Jan 2007 02:17:19 +0100 Subject: [PATCH 15/63] apply --cached: fix crash in subdirectory The static variable "prefix" was shadowed by an unused parameter of the same name. In case of execution in a subdirectory, the static variable was accessed, leading to a crash. Signed-off-by: Johannes Schindelin --- builtin-apply.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin-apply.c b/builtin-apply.c index 54fd2cb0c7..3fefdacd94 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -2589,7 +2589,7 @@ static int git_apply_config(const char *var, const char *value) } -int cmd_apply(int argc, const char **argv, const char *prefix) +int cmd_apply(int argc, const char **argv, const char *unused_prefix) { int i; int read_stdin = 1; From 06f6228a90c50618f0a9793c9e9695be15f93ae2 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 19 Jan 2007 23:52:06 -0800 Subject: [PATCH 16/63] Stop ignoring Documentation/README We do not copy this file from elsewhere anymore. Signed-off-by: Junio C Hamano --- Documentation/.gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/Documentation/.gitignore b/Documentation/.gitignore index a531be32dc..6a51331b2f 100644 --- a/Documentation/.gitignore +++ b/Documentation/.gitignore @@ -4,5 +4,4 @@ *.7 howto-index.txt doc.dep -README cmds-*.txt From 68025633e352264898de44ec4856552d9a3abece Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sat, 20 Jan 2007 03:09:34 +0100 Subject: [PATCH 17/63] Do not verify filenames in a bare repository For example, it makes no sense to check the presence of a file named "HEAD" when calling "git log HEAD" in a bare repository. Noticed by Han-Wen Nienhuys. Signed-off-by: Johannes Schindelin --- cache.h | 1 + setup.c | 26 ++++++++++++++++++++++++++ t/t1020-subdirectory.sh | 29 +++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+) diff --git a/cache.h b/cache.h index a218db3b30..473197ded8 100644 --- a/cache.h +++ b/cache.h @@ -129,6 +129,7 @@ extern int cache_errno; extern int is_bare_repository_cfg; extern int is_bare_repository(void); +extern int is_inside_git_dir(void); extern const char *get_git_dir(void); extern char *get_object_directory(void); extern char *get_refs_directory(void); diff --git a/setup.c b/setup.c index cc97f9f5c1..e9d3f5aab6 100644 --- a/setup.c +++ b/setup.c @@ -95,6 +95,8 @@ void verify_non_filename(const char *prefix, const char *arg) const char *name; struct stat st; + if (is_inside_git_dir()) + return; if (*arg == '-') return; /* flag */ name = prefix ? prefix_filename(prefix, strlen(prefix), arg) : arg; @@ -168,6 +170,28 @@ static int is_git_directory(const char *suspect) return 1; } +static int inside_git_dir = -1; + +int is_inside_git_dir(void) +{ + if (inside_git_dir < 0) { + char buffer[1024]; + + if (is_bare_repository()) + return (inside_git_dir = 1); + if (getcwd(buffer, sizeof(buffer))) { + const char *git_dir = get_git_dir(), *cwd = buffer; + while (*git_dir && *git_dir == *cwd) { + git_dir++; + cwd++; + } + inside_git_dir = !*git_dir; + } else + inside_git_dir = 0; + } + return inside_git_dir; +} + const char *setup_git_directory_gently(int *nongit_ok) { static char cwd[PATH_MAX+1]; @@ -206,6 +230,7 @@ const char *setup_git_directory_gently(int *nongit_ok) if (chdir(cwd)) die("Cannot come back to cwd"); setenv(GIT_DIR_ENVIRONMENT, cwd, 1); + inside_git_dir = 1; return NULL; } if (nongit_ok) { @@ -226,6 +251,7 @@ const char *setup_git_directory_gently(int *nongit_ok) offset++; cwd[len++] = '/'; cwd[len] = 0; + inside_git_dir = !strncmp(cwd + offset, ".git/", 5); return cwd + offset; } diff --git a/t/t1020-subdirectory.sh b/t/t1020-subdirectory.sh index 4409b87f8d..c090c96185 100755 --- a/t/t1020-subdirectory.sh +++ b/t/t1020-subdirectory.sh @@ -106,4 +106,33 @@ test_expect_success 'read-tree' ' cmp ../one ../original.one ' +test_expect_success 'no file/rev ambuguity check inside .git' ' + cd $HERE && + git commit -a -m 1 && + cd $HERE/.git && + git show -s HEAD +' + +test_expect_success 'no file/rev ambuguity check inside a bare repo' ' + cd $HERE && + git clone -s --bare .git foo.git && + cd foo.git && GIT_DIR=. git show -s HEAD +' + +# This still does not work as it should... +: test_expect_success 'no file/rev ambuguity check inside a bare repo' ' + cd $HERE && + git clone -s --bare .git foo.git && + cd foo.git && git show -s HEAD +' + +test_expect_success 'detection should not be fooled by a symlink' ' + cd $HERE && + rm -fr foo.git && + git clone -s .git another && + ln -s another yetanother && + cd yetanother/.git && + git show -s HEAD +' + test_done From 453c1e857534f90b88367f96fc8ca1e7841f9400 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 20 Jan 2007 10:47:41 -0800 Subject: [PATCH 18/63] git-tag -d: allow deleting multiple tags at once. Signed-off-by: Junio C Hamano --- Documentation/git-tag.txt | 8 ++++---- git-tag.sh | 21 +++++++++++++++------ 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt index 8a71ab37df..13c7aefbf3 100644 --- a/Documentation/git-tag.txt +++ b/Documentation/git-tag.txt @@ -3,14 +3,14 @@ git-tag(1) NAME ---- -git-tag - Create or verify a tag object signed with GPG +git-tag - Create, list, delete or verify a tag object signed with GPG SYNOPSIS -------- [verse] -'git-tag' [-a | -s | -u ] [-f | -d | -v] [-m | -F ] - [] +'git-tag' [-a | -s | -u ] [-f | -v] [-m | -F ] [] +'git-tag' -d ... 'git-tag' -l [] DESCRIPTION @@ -55,7 +55,7 @@ OPTIONS Replace an existing tag with the given name (instead of failing) -d:: - Delete an existing tag with the given name + Delete existing tags with the given names. -v:: Verify the gpg signature of given the tag diff --git a/git-tag.sh b/git-tag.sh index ecb9100e4b..94499c9b36 100755 --- a/git-tag.sh +++ b/git-tag.sh @@ -63,12 +63,21 @@ do ;; -d) shift - tag_name="$1" - tag=$(git-show-ref --verify --hash -- "refs/tags/$tag_name") || - die "Seriously, what tag are you talking about?" - git-update-ref -m 'tag: delete' -d "refs/tags/$tag_name" "$tag" && - echo "Deleted tag $tag_name." - exit $? + had_error=0 + for tag + do + cur=$(git-show-ref --verify --hash -- "refs/tags/$tag") || { + echo >&2 "Seriously, what tag are you talking about?" + had_error=1 + continue + } + git-update-ref -m 'tag: delete' -d "refs/tags/$tag" "$cur" || { + had_error=1 + continue + } + echo "Deleted tag $tag." + done + exit $had_error ;; -v) shift From 11a6ddb2c8a81815ee9d5411638487ee99770a0b Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 20 Jan 2007 10:51:37 -0800 Subject: [PATCH 19/63] branch -f: no reason to forbid updating the current branch in a bare repo. Signed-off-by: Junio C Hamano --- builtin-branch.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin-branch.c b/builtin-branch.c index c760e188ea..25ffa5491c 100644 --- a/builtin-branch.c +++ b/builtin-branch.c @@ -324,7 +324,7 @@ static void create_branch(const char *name, const char *start_name, if (resolve_ref(ref, sha1, 1, NULL)) { if (!force) die("A branch named '%s' already exists.", name); - else if (!strcmp(head, name)) + else if (!is_bare_repository() && !strcmp(head, name)) die("Cannot force update the current branch."); } From bcf316187699c5e97bf47c1b8a00c844bf809fbc Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 20 Jan 2007 19:11:29 -0800 Subject: [PATCH 20/63] git-rebase: allow rebasing a detached HEAD. Signed-off-by: Junio C Hamano --- git-rebase.sh | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/git-rebase.sh b/git-rebase.sh index c8bd0f99d1..99cedadda1 100755 --- a/git-rebase.sh +++ b/git-rebase.sh @@ -275,8 +275,12 @@ case "$#" in git-checkout "$2" || usage ;; *) - branch_name=`git symbolic-ref HEAD` || die "No current branch" - branch_name=`expr "z$branch_name" : 'zrefs/heads/\(.*\)'` + if branch_name=`git symbolic-ref -q HEAD` + then + branch_name=`expr "z$branch_name" : 'zrefs/heads/\(.*\)'` + else + branch_name=HEAD ;# detached + fi ;; esac branch=$(git-rev-parse --verify "${branch_name}^0") || exit From 8860fd42fcf5a7853f7d7c2198793183320293ff Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 11 Jan 2007 11:47:48 +0100 Subject: [PATCH 21/63] Teach the revision walker to walk by reflogs with --walk-reflogs When called with "--walk-reflogs", as long as there are reflogs available, the walker will take this information into account, rather than the parent information in the commit object. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- Makefile | 2 +- log-tree.c | 3 + reflog-walk.c | 235 ++++++++++++++++++++++++++++++++++++++++++++++++++ reflog-walk.h | 11 +++ revision.c | 11 +++ revision.h | 2 + 6 files changed, 263 insertions(+), 1 deletion(-) create mode 100644 reflog-walk.c create mode 100644 reflog-walk.h diff --git a/Makefile b/Makefile index 8432ab8ba1..1a29392a12 100644 --- a/Makefile +++ b/Makefile @@ -254,7 +254,7 @@ LIB_OBJS = \ interpolate.o \ lockfile.o \ object.o pack-check.o patch-delta.o path.o pkt-line.o sideband.o \ - reachable.o \ + reachable.o reflog-walk.o \ quote.o read-cache.o refs.o run-command.o dir.o object-refs.o \ 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 \ diff --git a/log-tree.c b/log-tree.c index 35be33aaf7..f043ad3723 100644 --- a/log-tree.c +++ b/log-tree.c @@ -2,6 +2,7 @@ #include "diff.h" #include "commit.h" #include "log-tree.h" +#include "reflog-walk.h" static void show_parents(struct commit *commit, int abbrev) { @@ -223,6 +224,8 @@ void show_log(struct rev_info *opt, const char *sep) printf("%s", diff_get_color(opt->diffopt.color_diff, DIFF_RESET)); putchar(opt->commit_format == CMIT_FMT_ONELINE ? ' ' : '\n'); + if (opt->reflog_info) + show_reflog_message(opt->reflog_info); } /* diff --git a/reflog-walk.c b/reflog-walk.c new file mode 100644 index 0000000000..d4b49c7354 --- /dev/null +++ b/reflog-walk.c @@ -0,0 +1,235 @@ +#include "cache.h" +#include "commit.h" +#include "refs.h" +#include "diff.h" +#include "revision.h" +#include "path-list.h" + +struct complete_reflogs { + char *ref; + struct reflog_info { + unsigned char osha1[20], nsha1[20]; + char *email; + unsigned long timestamp; + int tz; + char *message; + } *items; + int nr, alloc; +}; + +static int read_one_reflog(unsigned char *osha1, unsigned char *nsha1, + const char *email, unsigned long timestamp, int tz, + const char *message, void *cb_data) +{ + struct complete_reflogs *array = cb_data; + struct reflog_info *item; + + if (array->nr >= array->alloc) { + array->alloc = alloc_nr(array->nr + 1); + array->items = xrealloc(array->items, array->alloc * + sizeof(struct reflog_info)); + } + item = array->items + array->nr; + memcpy(item->osha1, osha1, 20); + memcpy(item->nsha1, nsha1, 20); + item->email = xstrdup(email); + item->timestamp = timestamp; + item->tz = tz; + item->message = xstrdup(message); + array->nr++; + return 0; +} + +static struct complete_reflogs *read_complete_reflog(const char *ref) +{ + struct complete_reflogs *reflogs = + xcalloc(sizeof(struct complete_reflogs), 1); + reflogs->ref = xstrdup(ref); + for_each_reflog_ent(ref, read_one_reflog, reflogs); + if (reflogs->nr == 0) { + unsigned char sha1[20]; + const char *name = resolve_ref(ref, sha1, 1, NULL); + if (name) + for_each_reflog_ent(name, read_one_reflog, reflogs); + } + if (reflogs->nr == 0) { + int len = strlen(ref); + char *refname = xmalloc(len + 12); + sprintf(refname, "refs/%s", ref); + for_each_reflog_ent(refname, read_one_reflog, reflogs); + if (reflogs->nr == 0) { + sprintf(refname, "refs/heads/%s", ref); + for_each_reflog_ent(refname, read_one_reflog, reflogs); + } + free(refname); + } + return reflogs; +} + +static int get_reflog_recno_by_time(struct complete_reflogs *array, + unsigned long timestamp) +{ + int i; + for (i = array->nr - 1; i >= 0; i++) + if (timestamp >= array->items[i].timestamp) + return i; + return -1; +} + +struct commit_info_lifo { + struct commit_info { + struct commit *commit; + void *util; + } *items; + int nr, alloc; +}; + +static struct commit_info *get_commit_info(struct commit *commit, + struct commit_info_lifo *lifo, int pop) +{ + int i; + for (i = 0; i < lifo->nr; i++) + if (lifo->items[i].commit == commit) { + struct commit_info *result = &lifo->items[i]; + if (pop) { + if (i + 1 < lifo->nr) + memmove(lifo->items + i, + lifo->items + i + 1, + (lifo->nr - i) * + sizeof(struct commit_info)); + lifo->nr--; + } + return result; + } + return NULL; +} + +static void add_commit_info(struct commit *commit, void *util, + struct commit_info_lifo *lifo) +{ + struct commit_info *info; + if (lifo->nr >= lifo->alloc) { + lifo->alloc = alloc_nr(lifo->nr + 1); + lifo->items = xrealloc(lifo->items, + lifo->alloc * sizeof(struct commit_info)); + } + info = lifo->items + lifo->nr; + info->commit = commit; + info->util = util; + lifo->nr++; +} + +struct commit_reflog { + int flag, recno; + struct complete_reflogs *reflogs; +}; + +struct reflog_walk_info { + struct commit_info_lifo reflogs; + struct path_list complete_reflogs; + struct commit_reflog *last_commit_reflog; +}; + +void init_reflog_walk(struct reflog_walk_info** info) +{ + *info = xcalloc(sizeof(struct reflog_walk_info), 1); +} + +void add_reflog_for_walk(struct reflog_walk_info *info, + struct commit *commit, const char *name) +{ + unsigned long timestamp = 0; + int recno = -1; + struct path_list_item *item; + struct complete_reflogs *reflogs; + char *branch, *at = strchr(name, '@'); + struct commit_reflog *commit_reflog; + + branch = xstrdup(name); + if (at && at[1] == '{') { + char *ep; + branch[at - name] = '\0'; + recno = strtoul(at + 2, &ep, 10); + if (*ep != '}') { + recno = -1; + timestamp = approxidate(at + 2); + } + } else + recno = 0; + + item = path_list_lookup(branch, &info->complete_reflogs); + if (item) + reflogs = item->util; + else { + reflogs = read_complete_reflog(branch); + if (!reflogs || reflogs->nr == 0) + die("No reflogs found for '%s'", branch); + path_list_insert(branch, &info->complete_reflogs)->util + = reflogs; + } + + commit_reflog = xcalloc(sizeof(struct commit_reflog), 1); + if (recno < 0) { + commit_reflog->flag = 1; + commit_reflog->recno = get_reflog_recno_by_time(reflogs, timestamp); + if (commit_reflog->recno < 0) { + free(branch); + free(commit_reflog); + return; + } + } else + commit_reflog->recno = reflogs->nr - recno - 1; + commit_reflog->reflogs = reflogs; + + add_commit_info(commit, commit_reflog, &info->reflogs); +} + +void fake_reflog_parent(struct reflog_walk_info *info, struct commit *commit) +{ + struct commit_info *commit_info = + get_commit_info(commit, &info->reflogs, 0); + struct commit_reflog *commit_reflog; + struct reflog_info *reflog; + + info->last_commit_reflog = NULL; + if (!commit_info) + return; + + commit_reflog = commit_info->util; + if (commit_reflog->recno < 0) { + commit->parents = NULL; + return; + } + + reflog = &commit_reflog->reflogs->items[commit_reflog->recno]; + info->last_commit_reflog = commit_reflog; + commit_reflog->recno--; + commit_info->commit = (struct commit *)parse_object(reflog->osha1); + if (!commit_info->commit) { + commit->parents = NULL; + return; + } + + commit->parents = xcalloc(sizeof(struct commit_list), 1); + commit->parents->item = commit_info->commit; + commit->object.flags &= ~(ADDED | SEEN | SHOWN); +} + +void show_reflog_message(struct reflog_walk_info* info) +{ + if (info && info->last_commit_reflog) { + struct commit_reflog *commit_reflog = info->last_commit_reflog; + struct reflog_info *info; + + printf("Reflog: %s@{", commit_reflog->reflogs->ref); + info = &commit_reflog->reflogs->items[commit_reflog->recno + 1]; + if (commit_reflog->flag) + printf("%s", show_rfc2822_date(info->timestamp, + info->tz)); + else + printf("%d", commit_reflog->reflogs->nr + - 2 - commit_reflog->recno); + printf("} (%s)\nReflog message: %s", + info->email, info->message); + } +} diff --git a/reflog-walk.h b/reflog-walk.h new file mode 100644 index 0000000000..787996b377 --- /dev/null +++ b/reflog-walk.h @@ -0,0 +1,11 @@ +#ifndef REFLOG_WALK_H +#define REFLOG_WALK_H + +extern void init_reflog_walk(struct reflog_walk_info** info); +extern void add_reflog_for_walk(struct reflog_walk_info *info, + struct commit *commit, const char *name); +extern void fake_reflog_parent(struct reflog_walk_info *info, + struct commit *commit); +extern void show_reflog_message(struct reflog_walk_info* info); + +#endif diff --git a/revision.c b/revision.c index f2ddd95e29..ebd025064c 100644 --- a/revision.c +++ b/revision.c @@ -7,6 +7,7 @@ #include "refs.h" #include "revision.h" #include "grep.h" +#include "reflog-walk.h" static char *path_name(struct name_path *path, const char *name) { @@ -116,6 +117,9 @@ void mark_parents_uninteresting(struct commit *commit) void add_pending_object(struct rev_info *revs, struct object *obj, const char *name) { add_object_array(obj, name, &revs->pending); + if (revs->reflog_info && obj->type == OBJ_COMMIT) + add_reflog_for_walk(revs->reflog_info, + (struct commit *)obj, name); } static struct object *get_reference(struct rev_info *revs, const char *name, const unsigned char *sha1, unsigned int flags) @@ -864,6 +868,10 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch handle_reflog(revs, flags); continue; } + if (!strcmp(arg, "--walk-reflogs")) { + init_reflog_walk(&revs->reflog_info); + continue; + } if (!strcmp(arg, "--not")) { flags ^= UNINTERESTING; continue; @@ -1210,6 +1218,9 @@ static struct commit *get_revision_1(struct rev_info *revs) revs->commits = entry->next; free(entry); + if (revs->reflog_info) + fake_reflog_parent(revs->reflog_info, commit); + /* * If we haven't done the list limiting, we need to look at * the parents here. We also need to do the date-based limiting diff --git a/revision.h b/revision.h index 8f7907d7ab..d93481f68f 100644 --- a/revision.h +++ b/revision.h @@ -89,6 +89,8 @@ struct rev_info { topo_sort_set_fn_t topo_setter; topo_sort_get_fn_t topo_getter; + + struct reflog_walk_info *reflog_info; }; #define REV_TREE_SAME 0 From db055e65d20b20f8f32eb85ee1a2417d3b95c1c7 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sat, 20 Jan 2007 03:28:19 +0100 Subject: [PATCH 22/63] --walk-reflogs: disallow uninteresting commits Do not allow uninteresting commits with --walk-reflogs, since it is not clear what should be shown in these cases: $ git log --walk-reflogs master..next $ git log --walk-reflogs ^master Signed-off-by: Johannes Schindelin --- reflog-walk.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/reflog-walk.c b/reflog-walk.c index d4b49c7354..2d974116de 100644 --- a/reflog-walk.c +++ b/reflog-walk.c @@ -145,6 +145,9 @@ void add_reflog_for_walk(struct reflog_walk_info *info, char *branch, *at = strchr(name, '@'); struct commit_reflog *commit_reflog; + if (commit->object.flags & UNINTERESTING) + die ("Cannot walk reflogs for %s", name); + branch = xstrdup(name); if (at && at[1] == '{') { char *ep; From 911cedc95c2990b75f010d0d7bdbbc5911831708 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 19 Jan 2007 23:21:32 -0800 Subject: [PATCH 23/63] log --walk-reflog: documentation 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 86c94e7dfd..fcc540bd3e 100644 --- a/Documentation/git-rev-list.txt +++ b/Documentation/git-rev-list.txt @@ -190,6 +190,21 @@ limiting may be applied. In addition to the '' listed on the command line, read them from the standard input. +--walk-reflogs:: + + Instead of walking the commit ancestry chain, walk + reflog entries from the most recent one to older ones. + When this option is used you cannot specify commits to + exclude (that is, '{caret}commit', 'commit1..commit2', + nor 'commit1...commit2' notations cannot be used). ++ +With '\--pretty' format other than oneline (for obvious reasons), +this causes the output to have two extra lines of information +taken from the reflog. By default, 'commit@{Nth}' notation is +used in the output. When the starting commit is specified as +'commit@{now}', output also uses 'commit@{timestamp}' notation +instead. + --merge:: After a failed merge, show refs that touch files having a From 53645a3a62977d5efd96ca9f3654985802212a9d Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 20 Jan 2007 00:47:34 -0800 Subject: [PATCH 24/63] reflog-walk: build fixes Dependency on reflog-walk.h was missing in the Makefile, and reflog-walk.c did not even include it. Signed-off-by: Junio C Hamano --- Makefile | 2 +- reflog-walk.c | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 1a29392a12..07246f3d18 100644 --- a/Makefile +++ b/Makefile @@ -241,7 +241,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 + utf8.h reflog-walk.h DIFF_OBJS = \ diff.o diff-lib.o diffcore-break.o diffcore-order.o \ diff --git a/reflog-walk.c b/reflog-walk.c index 2d974116de..989a7aee95 100644 --- a/reflog-walk.c +++ b/reflog-walk.c @@ -4,6 +4,7 @@ #include "diff.h" #include "revision.h" #include "path-list.h" +#include "reflog-walk.h" struct complete_reflogs { char *ref; From 4d12a471230625da73be464f5a20b7480a6b8ecb Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 20 Jan 2007 00:51:41 -0800 Subject: [PATCH 25/63] Fix --walk-reflog with --pretty=oneline Now, "git log --abbrev-commit --pretty=o --walk-reflogs HEAD" is reasonably pleasant to use. Signed-off-by: Junio C Hamano --- Documentation/git-rev-list.txt | 4 +++- log-tree.c | 3 ++- reflog-walk.c | 33 ++++++++++++++++++++++----------- reflog-walk.h | 2 +- 4 files changed, 28 insertions(+), 14 deletions(-) diff --git a/Documentation/git-rev-list.txt b/Documentation/git-rev-list.txt index fcc540bd3e..a996f6cb1e 100644 --- a/Documentation/git-rev-list.txt +++ b/Documentation/git-rev-list.txt @@ -27,6 +27,7 @@ SYNOPSIS [ \--pretty | \--header ] [ \--bisect ] [ \--merge ] + [ \--walk-reflogs ] ... [ \-- ... ] DESCRIPTION @@ -203,7 +204,8 @@ this causes the output to have two extra lines of information taken from the reflog. By default, 'commit@{Nth}' notation is used in the output. When the starting commit is specified as 'commit@{now}', output also uses 'commit@{timestamp}' notation -instead. +instead. Under '\--pretty=oneline', the commit message is +prefixed with this information on the same line. --merge:: diff --git a/log-tree.c b/log-tree.c index f043ad3723..c0fa096327 100644 --- a/log-tree.c +++ b/log-tree.c @@ -225,7 +225,8 @@ void show_log(struct rev_info *opt, const char *sep) diff_get_color(opt->diffopt.color_diff, DIFF_RESET)); putchar(opt->commit_format == CMIT_FMT_ONELINE ? ' ' : '\n'); if (opt->reflog_info) - show_reflog_message(opt->reflog_info); + show_reflog_message(opt->reflog_info, + opt->commit_format == CMIT_FMT_ONELINE);; } /* diff --git a/reflog-walk.c b/reflog-walk.c index 989a7aee95..8ccbe97760 100644 --- a/reflog-walk.c +++ b/reflog-walk.c @@ -219,21 +219,32 @@ void fake_reflog_parent(struct reflog_walk_info *info, struct commit *commit) commit->object.flags &= ~(ADDED | SEEN | SHOWN); } -void show_reflog_message(struct reflog_walk_info* info) +void show_reflog_message(struct reflog_walk_info* info, int oneline) { if (info && info->last_commit_reflog) { struct commit_reflog *commit_reflog = info->last_commit_reflog; struct reflog_info *info; - printf("Reflog: %s@{", commit_reflog->reflogs->ref); - info = &commit_reflog->reflogs->items[commit_reflog->recno + 1]; - if (commit_reflog->flag) - printf("%s", show_rfc2822_date(info->timestamp, - info->tz)); - else - printf("%d", commit_reflog->reflogs->nr - - 2 - commit_reflog->recno); - printf("} (%s)\nReflog message: %s", - info->email, info->message); + info = &commit_reflog->reflogs->items[commit_reflog->recno+1]; + if (oneline) { + printf("%s@{", commit_reflog->reflogs->ref); + if (commit_reflog->flag) + printf("%s", show_date(info->timestamp, 0, 1)); + else + printf("%d", commit_reflog->reflogs->nr + - 2 - commit_reflog->recno); + printf("}: "); + } + else { + printf("Reflog: %s@{", commit_reflog->reflogs->ref); + if (commit_reflog->flag) + printf("%s", show_rfc2822_date(info->timestamp, + info->tz)); + else + printf("%d", commit_reflog->reflogs->nr + - 2 - commit_reflog->recno); + printf("} (%s)\nReflog message: %s", + info->email, info->message); + } } } diff --git a/reflog-walk.h b/reflog-walk.h index 787996b377..e63d86778b 100644 --- a/reflog-walk.h +++ b/reflog-walk.h @@ -6,6 +6,6 @@ extern void add_reflog_for_walk(struct reflog_walk_info *info, struct commit *commit, const char *name); extern void fake_reflog_parent(struct reflog_walk_info *info, struct commit *commit); -extern void show_reflog_message(struct reflog_walk_info* info); +extern void show_reflog_message(struct reflog_walk_info *info, int); #endif From 40ab7c33cd6dfdf887ed15baee1325ee445eced7 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sat, 20 Jan 2007 10:49:15 +0100 Subject: [PATCH 26/63] --walk-reflogs: actually find the right commit by date. Embarassing thinko. Signed-off-by: Johannes Schindelin --- reflog-walk.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reflog-walk.c b/reflog-walk.c index 8ccbe97760..8e2cd2fcf6 100644 --- a/reflog-walk.c +++ b/reflog-walk.c @@ -71,7 +71,7 @@ static int get_reflog_recno_by_time(struct complete_reflogs *array, unsigned long timestamp) { int i; - for (i = array->nr - 1; i >= 0; i++) + for (i = array->nr - 1; i >= 0; i--) if (timestamp >= array->items[i].timestamp) return i; return -1; From a6c730644b7e1d35bd0d26962dbc978aa47d1863 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sat, 20 Jan 2007 22:28:16 +0100 Subject: [PATCH 27/63] --walk-reflogs: do not crash with cyclic reflog ancestry Since you can reset --hard to any revision you already had, when traversing the reflog ancestry, we may not free() the commit buffer. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- builtin-log.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/builtin-log.c b/builtin-log.c index f3cff13edc..13a3f9b25d 100644 --- a/builtin-log.c +++ b/builtin-log.c @@ -50,8 +50,11 @@ static int cmd_log_walk(struct rev_info *rev) prepare_revision_walk(rev); while ((commit = get_revision(rev)) != NULL) { log_tree_commit(rev, commit); - free(commit->buffer); - commit->buffer = NULL; + if (!rev->reflog_info) { + /* we allow cycles in reflog ancestry */ + free(commit->buffer); + commit->buffer = NULL; + } free_commit_list(commit->parents); commit->parents = NULL; } From 9b088c4e394df84232cfd37aea78349a495b09c1 Mon Sep 17 00:00:00 2001 From: Matthias Lederhofer Date: Fri, 19 Jan 2007 11:49:35 +0100 Subject: [PATCH 28/63] prune: --grace=time This option gives grace period to objects that are unreachable from the refs from getting pruned. The default value is 24 hours and may be changed using gc.prunegrace. Signed-off-by: Matthias Lederhofer Signed-off-by: Junio C Hamano --- Documentation/git-prune.txt | 9 ++++++++- builtin-prune.c | 31 ++++++++++++++++++++++++++++++- t/t1410-reflog.sh | 6 +++--- t/t5400-send-pack.sh | 4 ++-- t/t5700-clone-reference.sh | 4 ++-- t/t5710-info-alternate.sh | 6 +++--- 6 files changed, 48 insertions(+), 12 deletions(-) diff --git a/Documentation/git-prune.txt b/Documentation/git-prune.txt index a11e303094..fbd344da40 100644 --- a/Documentation/git-prune.txt +++ b/Documentation/git-prune.txt @@ -8,7 +8,7 @@ git-prune - Prunes all unreachable objects from the object database SYNOPSIS -------- -'git-prune' [-n] [--] [...] +'git-prune' [-n] [--grace=