Merge branch 'lt/tree-2'
authorJunio C Hamano <junkio@cox.net>
Sun, 4 Jun 2006 06:59:27 +0000 (23:59 -0700)
committerJunio C Hamano <junkio@cox.net>
Sun, 4 Jun 2006 06:59:27 +0000 (23:59 -0700)
* lt/tree-2:
  fetch.c: do not call process_tree() from process_tree().
  tree_entry(): new tree-walking helper function
  adjust to the rebased series by Linus.
  Remove "tree->entries" tree-entry list from tree parser
  Switch "read_tree_recursive()" over to tree-walk functionality
  Make "tree_entry" have a SHA1 instead of a union of object pointers
  Add raw tree buffer info to "struct tree"
  Remove last vestiges of generic tree_entry_list
  Convert fetch.c: process_tree() to raw tree walker
  Convert "mark_tree_uninteresting()" to raw tree walker
  Remove unused "zeropad" entry from tree_list_entry
  fsck-objects: avoid unnecessary tree_entry_list usage
  Remove "tree->entries" tree-entry list from tree parser
  builtin-read-tree.c: avoid tree_entry_list in prime_cache_tree_rec()
  Switch "read_tree_recursive()" over to tree-walk functionality
  Make "tree_entry" have a SHA1 instead of a union of object pointers
  Make "struct tree" contain the pointer to the tree buffer

80 files changed:
Documentation/config.txt
Documentation/core-tutorial.txt
Documentation/cvs-migration.txt
Documentation/everyday.txt
Documentation/git-apply.txt
Documentation/git-blame.txt
Documentation/git-branch.txt
Documentation/git-checkout.txt
Documentation/git-commit.txt
Documentation/git-cvsserver.txt
Documentation/git-daemon.txt
Documentation/git-diff-index.txt
Documentation/git-diff-tree.txt
Documentation/git-diff.txt
Documentation/git-format-patch.txt
Documentation/git-fsck-objects.txt
Documentation/git-grep.txt
Documentation/git-merge-index.txt
Documentation/git-patch-id.txt
Documentation/git-read-tree.txt
Documentation/git-repo-config.txt
Documentation/git-reset.txt
Documentation/git-rev-parse.txt
Documentation/git-send-email.txt
Documentation/git-send-pack.txt
Documentation/git-sh-setup.txt
Documentation/git-tools.txt
Documentation/git-update-index.txt
Documentation/git-update-ref.txt
Documentation/hooks.txt
Documentation/repository-layout.txt
Documentation/technical/pack-format.txt
Documentation/technical/pack-heuristics.txt
Documentation/tutorial-2.txt
Documentation/tutorial.txt
Makefile
builtin-log.c
builtin-read-tree.c
builtin-rev-parse.c [moved from rev-parse.c with 98% similarity]
builtin.h
cache.h
config.c
contrib/git-svn/git-svn.perl
contrib/git-svn/t/t0001-contrib-git-svn-props.sh
environment.c
exec_cmd.c
fetch.c
fetch.h
git-am.sh
git-applypatch.sh
git-branch.sh
git-checkout.sh
git-clean.sh
git-commit.sh
git-fetch.sh
git-reset.sh
git-send-email.perl
git-svnimport.perl
git.c
gitk
http-fetch.c
http.c
local-fetch.c
log-tree.c
mailsplit.c
refs.c
refs.h
revision.h
sha1_file.c
sha1_name.c
ssh-fetch.c
t/t1400-update-ref.sh [new file with mode: 0755]
t/t2101-update-index-reupdate.sh
t/t3200-branch.sh
t/t3300-funny-names.sh
t/t4012-diff-binary.sh
t/t5500-fetch-pack.sh
t/t6000lib.sh
t/t9001-send-email.sh [new file with mode: 0755]
update-ref.c

index d1a4bec..c861c6c 100644 (file)
@@ -2,7 +2,7 @@ CONFIGURATION FILE
 ------------------
 
 The git configuration file contains a number of variables that affect
-the git commands behaviour. They can be used by both the git plumbing
+the git commands behavior. They can be used by both the git plumbing
 and the porcelains. The variables are divided to sections, where
 in the fully qualified variable name the variable itself is the last
 dot-separated segment and the section name is everything before the last
@@ -53,7 +53,7 @@ core.gitProxy::
        may be set multiple times and is matched in the given order;
        the first match wins.
 
-       Can be overriden by the 'GIT_PROXY_COMMAND' environment variable
+       Can be overridden by the 'GIT_PROXY_COMMAND' environment variable
        (which always applies universally, without the special "for"
        handling).
 
@@ -70,6 +70,14 @@ core.preferSymlinkRefs::
        This is sometimes needed to work with old scripts that
        expect HEAD to be a symbolic link.
 
+core.logAllRefUpdates::
+       If true, `git-update-ref` will append a line to
+       "$GIT_DIR/logs/<ref>" listing the new SHA1 and the date/time
+       of the update.  If the file does not exist it will be
+       created automatically.  This information can be used to
+       determine what commit was the tip of a branch "2 days ago".
+       This value is false by default (no logging).
+
 core.repositoryFormatVersion::
        Internal variable identifying the repository format and layout
        version.
@@ -115,12 +123,12 @@ http.sslCert::
 
 http.sslKey::
        File containing the SSL private key when fetching or pushing
-       over HTTPS. Can be overriden by the 'GIT_SSL_KEY' environment
+       over HTTPS. Can be overridden by the 'GIT_SSL_KEY' environment
        variable.
 
 http.sslCAInfo::
        File containing the certificates to verify the peer with when
-       fetching or pushing over HTTPS. Can be overriden by the
+       fetching or pushing over HTTPS. Can be overridden by the
        'GIT_SSL_CAINFO' environment variable.
 
 http.sslCAPath::
@@ -129,13 +137,13 @@ http.sslCAPath::
        by the 'GIT_SSL_CAPATH' environment variable.
 
 http.maxRequests::
-       How many HTTP requests to launch in parallel. Can be overriden
+       How many HTTP requests to launch in parallel. Can be overridden
        by the 'GIT_HTTP_MAX_REQUESTS' environment variable. Default is 5.
 
 http.lowSpeedLimit, http.lowSpeedTime::
        If the HTTP transfer speed is less than 'http.lowSpeedLimit'
        for longer than 'http.lowSpeedTime' seconds, the transfer is aborted.
-       Can be overriden by the 'GIT_HTTP_LOW_SPEED_LIMIT' and
+       Can be overridden by the 'GIT_HTTP_LOW_SPEED_LIMIT' and
        'GIT_HTTP_LOW_SPEED_TIME' environment variables.
 
 i18n.commitEncoding::
@@ -166,12 +174,12 @@ showbranch.default::
 
 user.email::
        Your email address to be recorded in any newly created commits.
-       Can be overriden by the 'GIT_AUTHOR_EMAIL' and 'GIT_COMMITTER_EMAIL'
+       Can be overridden by the 'GIT_AUTHOR_EMAIL' and 'GIT_COMMITTER_EMAIL'
        environment variables.  See gitlink:git-commit-tree[1].
 
 user.name::
        Your full name to be recorded in any newly created commits.
-       Can be overriden by the 'GIT_AUTHOR_NAME' and 'GIT_COMMITTER_NAME'
+       Can be overridden by the 'GIT_AUTHOR_NAME' and 'GIT_COMMITTER_NAME'
        environment variables.  See gitlink:git-commit-tree[1].
 
 whatchanged.difftree::
index d1360ec..1185897 100644 (file)
@@ -1,5 +1,5 @@
-A short git tutorial
-====================
+A git core tutorial for developers
+==================================
 
 Introduction
 ------------
@@ -184,7 +184,7 @@ $ git-cat-file -t 557db03de997c86a4a028e1ebd3a1ceb225be238
 ----------------
 
 where the `-t` tells `git-cat-file` to tell you what the "type" of the
-object is. git will tell you that you have a "blob" object (ie just a
+object is. git will tell you that you have a "blob" object (i.e., just a
 regular file), and you can see the contents with
 
 ----------------
@@ -619,7 +619,7 @@ $ git tag -s <tagname>
 ----------------
 
 which will sign the current `HEAD` (but you can also give it another
-argument that specifies the thing to tag, ie you could have tagged the
+argument that specifies the thing to tag, i.e., you could have tagged the
 current `mybranch` point by using `git tag <tagname> mybranch`).
 
 You normally only do signed tags for major releases or things
@@ -1097,7 +1097,7 @@ commit object by downloading from `repo.git/objects/xx/xxx\...`
 using the object name of that commit object.  Then it reads the
 commit object to find out its parent commits and the associate
 tree object; it repeats this process until it gets all the
-necessary objects.  Because of this behaviour, they are
+necessary objects.  Because of this behavior, they are
 sometimes also called 'commit walkers'.
 +
 The 'commit walkers' are sometimes also called 'dumb
index fa94efd..826d089 100644 (file)
@@ -1,7 +1,7 @@
 git for CVS users
 =================
 
-So you're a CVS user. That's ok, it's a treatable condition.  The job of
+So you're a CVS user. That's OK, it's a treatable condition.  The job of
 this document is to put you on the road to recovery, by helping you
 convert an existing cvs repository to git, and by showing you how to use a
 git repository in a cvs-like fashion.
@@ -159,7 +159,7 @@ other than `master`.
 
 [NOTE]
 ============
-Because of this behaviour, if the shared repository and the developer's
+Because of this behavior, if the shared repository and the developer's
 repository both have branches named `origin`, then a push like the above
 attempts to update the `origin` branch in the shared repository from the
 developer's `origin` branch.  The results may be unexpected, so it's
index 4b56370..2ad2d61 100644 (file)
@@ -66,7 +66,7 @@ $ git prune <4>
 <1> running without "--full" is usually cheap and assures the
 repository health reasonably well.
 <2> check how many loose objects there are and how much
-diskspace is wasted by not repacking.
+disk space is wasted by not repacking.
 <3> without "-a" repacks incrementally.  repacking every 4-5MB
 of loose objects accumulation may be a good rule of thumb.
 <4> after repack, prune removes the duplicate loose objects.
@@ -86,7 +86,7 @@ Individual Developer (Standalone)[[Individual Developer (Standalone)]]
 ----------------------------------------------------------------------
 
 A standalone individual developer does not exchange patches with
-other poeple, and works alone in a single repository, using the
+other people, and works alone in a single repository, using the
 following commands.
 
   * gitlink:git-show-branch[1] to see where you are.
@@ -370,7 +370,7 @@ Examples
 Run git-daemon to serve /pub/scm from inetd.::
 +
 ------------
-$ grep git /etc/inet.conf
+$ grep git /etc/inetd.conf
 git    stream  tcp     nowait  nobody \
   /usr/bin/git-daemon git-daemon --inetd --syslog --export-all /pub/scm
 ------------
index e93ea1f..9cc7c74 100644 (file)
@@ -113,7 +113,7 @@ OPTIONS
        When `git-apply` is used for statistics and not applying a
        patch, it defaults to `nowarn`.
        You can use different `<option>` to control this
-       behaviour:
+       behavior:
 +
 * `nowarn` turns off the trailing whitespace warning.
 * `warn` outputs warnings for a few such errors, but applies the
index 5189878..0a1fa00 100644 (file)
@@ -16,7 +16,7 @@ which introduced the line. Start annotation from the given revision.
 
 OPTIONS
 -------
--c, --compability::
+-c, --compatibility::
        Use the same output mode as git-annotate (Default: off).
 
 -l, --long::
index 0b37e2b..d43ef1d 100644 (file)
@@ -9,7 +9,7 @@ SYNOPSIS
 --------
 [verse]
 'git-branch' [-r]
-'git-branch' [-f] <branchname> [<start-point>]
+'git-branch' [-l] [-f] <branchname> [<start-point>]
 'git-branch' (-d | -D) <branchname>...
 
 DESCRIPTION
@@ -23,7 +23,8 @@ If no <start-point> is given, the branch will be created with a head
 equal to that of the currently checked out branch.
 
 With a `-d` or `-D` option, `<branchname>` will be deleted.  You may
-specify more than one branch for deletion.
+specify more than one branch for deletion.  If the branch currently
+has a ref log then the ref log will also be deleted.
 
 
 OPTIONS
@@ -34,6 +35,11 @@ OPTIONS
 -D::
        Delete a branch irrespective of its index status.
 
+-l::
+       Create the branch's ref log.  This activates recording of
+       all changes to made the branch ref, enabling use of date
+       based sha1 expressions such as "<branchname>@{yesterday}".
+
 -f::
        Force the creation of a new branch even if it means deleting
        a branch that already exists with the same name.
index d82efc0..fbdbadc 100644 (file)
@@ -8,7 +8,7 @@ git-checkout - Checkout and switch to a branch
 SYNOPSIS
 --------
 [verse]
-'git-checkout' [-f] [-b <new_branch>] [-m] [<branch>]
+'git-checkout' [-f] [-b <new_branch> [-l]] [-m] [<branch>]
 'git-checkout' [-m] [<branch>] <paths>...
 
 DESCRIPTION
@@ -40,6 +40,11 @@ OPTIONS
        by gitlink:git-check-ref-format[1].  Some of these checks
        may restrict the characters allowed in a branch name.
 
+-l::
+       Create the new branch's ref log.  This activates recording of
+       all changes to made the branch ref, enabling use of date
+       based sha1 expressions such as "<branchname>@{yesterday}".
+
 -m::
        If you have local modifications to one or more files that
        are different between the current branch and the branch to
index 38df59c..0fe66f2 100644 (file)
@@ -98,7 +98,7 @@ but can be used to amend a merge commit.
        Instead of committing only the files specified on the
        command line, update them in the index file and then
        commit the whole index.  This is the traditional
-       behaviour.
+       behavior.
 
 -o|--only::
        Commit only the files specified on the command line.
index 4dc13c3..952635d 100644 (file)
@@ -94,7 +94,7 @@ To get a checkout with the Eclipse CVS client:
 4. Pick 'HEAD' when it asks what branch/tag to check out. Untick the
    "launch commit wizard" to avoid committing the .project file.
 
-Protocol notes: If you are using anonymous acces via pserver, just select that.
+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',
index 924a676..4c357da 100644 (file)
@@ -20,7 +20,7 @@ aka 9418. It waits for a connection, and will just execute "git-upload-pack"
 when it gets one.
 
 It's careful in that there's a magic request-line that gives the command and
-what directory to upload, and it verifies that the directory is ok.
+what directory to upload, and it verifies that the directory is OK.
 
 It verifies that the directory has the magic file "git-daemon-export-ok", and
 it will refuse to export any git directory that hasn't explicitly been marked
@@ -28,7 +28,7 @@ for export this way (unless the '--export-all' parameter is specified). If you
 pass some directory paths as 'git-daemon' arguments, you can further restrict
 the offers to a whitelist comprising of those.
 
-This is ideally suited for read-only updates, ie pulling from git repositories.
+This is ideally suited for read-only updates, i.e., pulling from git repositories.
 
 OPTIONS
 -------
index 5d2096a..9cd43f1 100644 (file)
@@ -101,7 +101,7 @@ have not actually done a "git-update-index" on it yet - there is no
   torvalds@ppc970:~/v2.6/linux> git-diff-index HEAD
   *100644->100664 blob    7476bb......->000000......      kernel/sched.c
 
-ie it shows that the tree has changed, and that `kernel/sched.c` has is
+i.e., it shows that the tree has changed, and that `kernel/sched.c` has is
 not up-to-date and may contain new stuff. The all-zero sha1 means that to
 get the real diff, you need to look at the object in the working directory
 directly rather than do an object-to-object diff.
index 906830d..f7e8ff2 100644 (file)
@@ -32,7 +32,7 @@ include::diff-options.txt[]
 <path>...::
        If provided, the results are limited to a subset of files
        matching one of these prefix strings.
-       ie file matches `/^<pattern1>|<pattern2>|.../`
+       i.e., file matches `/^<pattern1>|<pattern2>|.../`
        Note that this parameter does not provide any wildcard or regexp
        features.
 
@@ -54,7 +54,7 @@ include::diff-options.txt[]
 +
 When a single commit is given on one line of such input, it compares
 the commit with its parents.  The following flags further affects its
-behaviour.  This does not apply to the case where two <tree-ish>
+behavior.  This does not apply to the case where two <tree-ish>
 separated with a single space are given.
 
 -m::
index 7267bcd..7ab2080 100644 (file)
@@ -85,7 +85,7 @@ $ git diff arch/i386 include/asm-i386   <3>
 nor deletion.
 <2> show only names and the nature of change, but not actual
 diff output.  --name-status disables usual patch generation
-which in turn also disables recursive behaviour, so without -r
+which in turn also disables recursive behavior, so without -r
 you would only see the directory name if there is a change in a
 file in a subdirectory.
 <3> limit diff output to named subtrees.
index 7cc7faf..493cac2 100644 (file)
@@ -9,37 +9,46 @@ git-format-patch - Prepare patches for e-mail submission
 SYNOPSIS
 --------
 [verse]
-'git-format-patch' [-n | -k] [-o <dir> | --stdout] [--attach] [-s] [-c]
-                [--diff-options] <his> [<mine>]
+'git-format-patch' [-n | -k] [-o <dir> | --stdout] [--attach]
+                  [-s | --signoff] [--diff-options] [--start-number <n>]
+                  <since>[..<until>]
 
 DESCRIPTION
 -----------
-Prepare each commit with its patch since <mine> head forked from
-<his> head, one file per patch formatted to resemble UNIX mailbox
-format, for e-mail submission or use with gitlink:git-am[1].
+
+Prepare each commit between <since> and <until> with its patch in
+one file per commit, formatted to resemble UNIX mailbox format.
+If ..<until> is not specified, the head of the current working
+tree is implied.
+
+The output of this command is convenient for e-mail submission or
+for use with gitlink:git-am[1].
 
 Each output file is numbered sequentially from 1, and uses the
-first line of the commit message (massaged for pathname safety)
-as the filename.
+first line of the commit message (massaged for pathname safety) as
+the filename. The names of the output files are printed to standard
+output, unless the --stdout option is specified.
 
-When -o is specified, output files are created in <dir>; otherwise
-they are created in the current working directory.  This option
-is ignored if --stdout is specified.
+If -o is specified, output files are created in <dir>.  Otherwise
+they are created in the current working directory.
 
-When -n is specified, instead of "[PATCH] Subject", the first
-line is formatted as "[PATCH N/M] Subject", unless you have only
-one patch.
+If -n is specified, instead of "[PATCH] Subject", the first line
+is formatted as "[PATCH n/m] Subject".
 
 
 OPTIONS
 -------
 -o|--output-directory <dir>::
        Use <dir> to store the resulting files, instead of the
-       current working directory.
+       current working directory. This option is ignored if
+       --stdout is specified.
 
 -n|--numbered::
        Name output in '[PATCH n/m]' format.
 
+--start-number <n>::
+       Start numbering the patches at <n> instead of 1.
+
 -k|--keep-subject::
        Do not strip/add '[PATCH]' from the first line of the
        commit log message.
@@ -48,17 +57,9 @@ OPTIONS
        Add `Signed-off-by:` line to the commit message, using
        the committer identity of yourself.
 
--c|--check::
-        Display suspicious lines in the patch.  The definition
-        of 'suspicious lines' is currently the lines that has
-        trailing whitespaces, and the lines whose indentation
-        has a SP character immediately followed by a TAB
-        character.
-
 --stdout::
-       This flag generates the mbox formatted output to the
-       standard output, instead of saving them into a file per
-       patch and implies --mbox.
+       Print all commits to the standard output in mbox format,
+       instead of creating a file for each one.
 
 --attach::
        Create attachments instead of inlining patches.
@@ -82,18 +83,18 @@ git-format-patch -k --stdout R1..R2 | git-am -3 -k::
        cherry-pick them.
 
 git-format-patch origin::
-       Extract commits the current branch accumulated since it
-       pulled from origin the last time in a patch form for
-       e-mail submission.
+       Extract all commits which are in the current branch but
+       not in the origin branch.  For each commit a separate file
+       is created in the current directory.
 
 git-format-patch -M -B origin::
-       The same as the previous one, except detect and handle
-       renames and complete rewrites intelligently to produce
-       renaming patch.  A renaming patch reduces the amount of
-       text output, and generally makes it easier to review
-       it.  Note that the "patch" program does not understand
-       renaming patch well, so use it only when you know the
-       recipient uses git to apply your patch.
+       The same as the previous one.  Additionally, it detects
+       and handles renames and complete rewrites intelligently to
+       produce a renaming patch.  A renaming patch reduces the
+       amount of text output, and generally makes it easier to
+       review it.  Note that the "patch" program does not
+       understand renaming patches, so use it only when you know
+       the recipient uses git to apply your patch.
 
 
 See Also
index 93ce9dc..d0af99d 100644 (file)
@@ -71,7 +71,7 @@ sorted properly etc), but on the whole if "git-fsck-objects" is happy, you
 do have a valid tree.
 
 Any corrupt objects you will have to find in backups or other archives
-(ie you can just remove them and do an "rsync" with some other site in
+(i.e., you can just remove them and do an "rsync" with some other site in
 the hopes that somebody else has the object you have corrupted).
 
 Of course, "valid tree" doesn't mean that it wasn't generated by some
index 74102b7..7b810df 100644 (file)
@@ -30,7 +30,7 @@ OPTIONS
 -------
 --cached::
        Instead of searching in the working tree files, check
-       the blobs registerd in the index file.
+       the blobs registered in the index file.
 
 -a | --text::
        Process binary files as if they were text.
@@ -65,7 +65,7 @@ OPTIONS
 -[ABC] <context>::
        Show `context` trailing (`A` -- after), or leading (`B`
        -- before), or both (`C` -- context) lines, and place a
-       line containing `--` between continguous groups of
+       line containing `--` between contiguous groups of
        matches.
 
 -f <file>::
index 332e023..6cd0601 100644 (file)
@@ -69,7 +69,7 @@ or
   fatal: merge program failed
 
 where the latter example shows how "git-merge-index" will stop trying to
-merge once anything has returned an error (ie "cat" returned an error
+merge once anything has returned an error (i.e., "cat" returned an error
 for the AA file, because it didn't exist in the original, and thus
 "git-merge-index" didn't even try to merge the MM thing).
 
index 723b8cc..5389097 100644 (file)
@@ -13,7 +13,7 @@ DESCRIPTION
 -----------
 A "patch ID" is nothing but a SHA1 of the diff associated with a patch, with
 whitespace and line numbers ignored.  As such, it's "reasonably stable", but at
-the same time also reasonably unique, ie two patches that have the same "patch
+the same time also reasonably unique, i.e., two patches that have the same "patch
 ID" are almost guaranteed to be the same thing.
 
 IOW, you can use this thing to look for likely duplicate commits.
index 844cfda..02c7e99 100644 (file)
@@ -205,7 +205,7 @@ The `git-write-tree` command refuses to write a nonsensical tree, and it
 will complain about unmerged entries if it sees a single entry that is not
 stage 0.
 
-Ok, this all sounds like a collection of totally nonsensical rules,
+OK, this all sounds like a collection of totally nonsensical rules,
 but it's actually exactly what you want in order to do a fast
 merge. The different stages represent the "result tree" (stage 0, aka
 "merged"), the original tree (stage 1, aka "orig"), and the two trees
@@ -226,7 +226,7 @@ populated.  Here is an outline of how the algorithm works:
 
 - the index file saves and restores with all this information, so you
   can merge things incrementally, but as long as it has entries in
-  stages 1/2/3 (ie "unmerged entries") you can't write the result. So
+  stages 1/2/3 (i.e., "unmerged entries") you can't write the result. So
   now the merge algorithm ends up being really simple:
 
   * you walk the index in order, and ignore all entries of stage 0,
index 660c18f..d5142e0 100644 (file)
@@ -49,7 +49,7 @@ OPTIONS
 -------
 
 --replace-all::
-       Default behaviour is to replace at most one line. This replaces
+       Default behavior is to replace at most one line. This replaces
        all lines matching the key (and optionally the value_regex).
 
 --get::
index b27399d..73a0ffc 100644 (file)
@@ -24,7 +24,7 @@ gitlink:git-revert[1] is your friend.
 OPTIONS
 -------
 --mixed::
-       Resets the index but not the working tree (ie, the changed files
+       Resets the index but not the working tree (i.e., the changed files
        are preserved but not marked for commit) and reports what has not
        been updated. This is the default action.
 
index ab896fc..627cde8 100644 (file)
@@ -13,7 +13,7 @@ SYNOPSIS
 DESCRIPTION
 -----------
 
-Many git Porcelainish commands take mixture of flags
+Many git porcelainish commands take mixture of flags
 (i.e. parameters that begin with a dash '-') and parameters
 meant for underlying `git-rev-list` command they use internally
 and flags and parameters for other commands they use as the
@@ -91,7 +91,7 @@ OPTIONS
 
 --short, --short=number::
        Instead of outputting the full SHA1 values of object names try to
-       abbriviate them to a shorter unique name. When no length is specified
+       abbreviate them to a shorter unique name. When no length is specified
        7 is used. The minimum length is 4.
 
 --since=datestring, --after=datestring::
@@ -124,6 +124,13 @@ syntax.
   happen to have both heads/master and tags/master, you can
   explicitly say 'heads/master' to tell git which one you mean.
 
+* A suffix '@' followed by a date specification enclosed in a brace
+  pair (e.g. '\{yesterday\}', '\{1 month 2 weeks 3 days 1 hour 1
+  second ago\}' or '\{1979-02-26 18:30:00\}') to specify the value
+  of the ref at a prior point in time.  This suffix may only be
+  used immediately following a ref name and the ref must have an
+  existing log ($GIT_DIR/logs/<ref>).
+
 * A suffix '{caret}' to a revision parameter means the first parent of
   that commit object.  '{caret}<n>' means the <n>th parent (i.e.
   'rev{caret}'
index 8c58685..ad1b9cf 100644 (file)
@@ -52,7 +52,7 @@ The options available are:
        is not set, this will be prompted for.
 
 --no-signed-off-by-cc::
-       Do not add emails foudn in Signed-off-by: lines to the cc list.
+       Do not add emails found in Signed-off-by: lines to the cc list.
 
 --quiet::
        Make git-send-email less verbose.  One line per email should be
index 08e0705..9e67f17 100644 (file)
@@ -53,7 +53,7 @@ Specifying the Refs
 There are three ways to specify which refs to update on the
 remote end.
 
-With '--all' flag, all refs that exist locally are transfered to
+With '--all' flag, all refs that exist locally are transferred to
 the remote side.  You cannot specify any '<ref>' if you use
 this flag.
 
index 6742c9b..79217d8 100644 (file)
@@ -13,7 +13,7 @@ DESCRIPTION
 -----------
 
 Sets up the normal git environment variables and a few helper functions
-(currently just "die()"), and returns ok if it all looks like a git archive.
+(currently just "die()"), and returns OK if it all looks like a git archive.
 So, to make the rest of the git scripts more careful and readable,
 use it as follows:
 
index 00e57a6..d79523f 100644 (file)
@@ -12,7 +12,7 @@ This document presents a brief summary of each tool and the corresponding
 link.
 
 
-Alternative/Augmentative Procelains
+Alternative/Augmentative Porcelains
 -----------------------------------
 
    - *Cogito* (http://www.kernel.org/pub/software/scm/cogito/)
index d043e86..3ae6e74 100644 (file)
@@ -40,7 +40,7 @@ OPTIONS
 --remove::
        If a specified file is in the index but is missing then it's
        removed.
-       Default behaviour is to ignore removed file.
+       Default behavior is to ignore removed file.
 
 --refresh::
        Looks at the current index and checks to see if merges or
index 475237f..e062030 100644 (file)
@@ -7,7 +7,7 @@ git-update-ref - update the object name stored in a ref safely
 
 SYNOPSIS
 --------
-'git-update-ref' <ref> <newvalue> [<oldvalue>]
+'git-update-ref' [-m <reason>] <ref> <newvalue> [<oldvalue>]
 
 DESCRIPTION
 -----------
@@ -49,6 +49,32 @@ for reading but not for writing (so we'll never write through a
 ref symlink to some other tree, if you have copied a whole
 archive by creating a symlink tree).
 
+Logging Updates
+---------------
+If config parameter "core.logAllRefUpdates" is true or the file
+"$GIT_DIR/logs/<ref>" exists then `git-update-ref` will append
+a line to the log file "$GIT_DIR/logs/<ref>" (dereferencing all
+symbolic refs before creating the log name) describing the change
+in ref value.  Log lines are formatted as:
+
+    . oldsha1 SP newsha1 SP committer LF
++
+Where "oldsha1" is the 40 character hexadecimal value previously
+stored in <ref>, "newsha1" is the 40 character hexadecimal value of
+<newvalue> and "committer" is the committer's name, email address
+and date in the standard GIT committer ident format.
+
+Optionally with -m:
+
+    . oldsha1 SP newsha1 SP committer TAB message LF
++
+Where all fields are as described above and "message" is the
+value supplied to the -m option.
+
+An update will fail (without changing <ref>) if the current user is
+unable to create a new log file, append to the existing log file
+or does not have committer information available.
+
 Author
 ------
 Written by Linus Torvalds <torvalds@osdl.org>.
index 3824a95..e3dde39 100644 (file)
@@ -100,7 +100,7 @@ update
 This hook is invoked by `git-receive-pack` on the remote repository,
 which is happens when a `git push` is done on a local repository.
 Just before updating the ref on the remote repository, the update hook
-is invoked.  It's exit status determins the success or failure of
+is invoked.  It's exit status determines the success or failure of
 the ref update.
 
 The hook executes once for each ref to be updated, and takes
@@ -151,7 +151,7 @@ so it is a poor place to do log old..new.
 
 The default post-update hook, when enabled, runs
 `git-update-server-info` to keep the information used by dumb
-transports (eg, http) up-to-date.  If you are publishing
+transports (e.g., http) up-to-date.  If you are publishing
 a git repository that is accessible via http, you should
 probably enable this hook.
 
index 98fbe7d..b52dfdc 100644 (file)
@@ -128,3 +128,14 @@ remotes::
        Stores shorthands to be used to give URL and default
        refnames to interact with remote repository to `git
        fetch`, `git pull` and `git push` commands.
+
+logs::
+       Records of changes made to refs are stored in this
+       directory.  See the documentation on git-update-ref
+       for more information.
+
+logs/refs/heads/`name`::
+       Records all changes made to the branch tip named `name`.
+
+logs/refs/tags/`name`::
+       Records all changes made to the tag named `name`.
index ed2decc..0e1ffb2 100644 (file)
@@ -5,8 +5,13 @@ GIT pack format
 
    - The header appears at the beginning and consists of the following:
 
-     4-byte signature
-     4-byte version number (network byte order)
+     4-byte signature:
+         The signature is: {'P', 'A', 'C', 'K'}
+
+     4-byte version number (network byte order):
+         GIT currently accepts version number 2 or 3 but
+         generates version 2 only.
+
      4-byte number of objects contained in the pack (network byte order)
 
      Observation: we cannot have more than 4G versions ;-) and
@@ -41,7 +46,7 @@ GIT pack format
     8-byte integers to go beyond 4G objects per pack, but it is
     not strictly necessary.
 
-  - The header is followed by sorted 28-byte entries, one entry
+  - The header is followed by sorted 24-byte entries, one entry
     per object in the pack.  Each entry is:
 
     4-byte network byte order integer, recording where the
index eaab3ee..9aadd5c 100644 (file)
@@ -288,7 +288,7 @@ And of course there is the "Other Shoe" Factor too.
         - we actively try to generate deltas from a larger object to a
           smaller one
         - this means that the top-of-tree very seldom has deltas
-          (ie deltas in _practice_ are "backwards deltas")
+          (i.e. deltas in _practice_ are "backwards deltas")
 
 Again, we should reread that whole paragraph.  Not just because
 Linus has slipped Linus's Law in there on us, but because it is
index 08d3453..82c6922 100644 (file)
@@ -49,7 +49,7 @@ tree
 
 A tree can refer to one or more "blob" objects, each corresponding to
 a file.  In addition, a tree can also refer to other tree objects,
-thus creating a directory heirarchy.  You can examine the contents of
+thus creating a directory hierarchy.  You can examine the contents of
 any tree using ls-tree (remember that a long enough initial portion
 of the SHA1 will also work):
 
@@ -377,7 +377,7 @@ At this point you should know everything necessary to read the man
 pages for any of the git commands; one good place to start would be
 with the commands mentioned in link:everyday.html[Everyday git].  You
 should be able to find any unknown jargon in the
-link:glossary.html[Glosssay].
+link:glossary.html[Glossary].
 
 The link:cvs-migration.html[CVS migration] document explains how to
 import a CVS repository into git, and shows how to use git in a
index 79781ad..db56312 100644 (file)
@@ -194,7 +194,7 @@ $ git clone /home/alice/project myrepo
 
 This creates a new directory "myrepo" containing a clone of Alice's
 repository.  The clone is on an equal footing with the original
-project, posessing its own copy of the original project's history.
+project, possessing its own copy of the original project's history.
 
 Bob then makes some changes and commits them:
 
@@ -240,7 +240,7 @@ $ git log -p master..bob-incoming
 shows a list of all the changes that Bob made since he branched from
 Alice's master branch.
 
-After examing those changes, and possibly fixing things, Alice can
+After examining those changes, and possibly fixing things, Alice can
 pull the changes into her master branch:
 
 -------------------------------------
@@ -374,7 +374,7 @@ project, so
 $ git grep "hello" v2.5
 -------------------------------------
 
-searches for all occurences of "hello" in v2.5.
+searches for all occurrences of "hello" in v2.5.
 
 If you leave out the commit name, git grep will search any of the
 files it manages in your current directory.  So
@@ -429,16 +429,24 @@ $ gitk --since="2 weeks ago" drivers/
 -------------------------------------
 
 allows you to browse any commits from the last 2 weeks of commits
-that modified files under the "drivers" directory.
+that modified files under the "drivers" directory.  (Note: you can
+adjust gitk's fonts by holding down the control key while pressing
+"-" or "+".)
 
 Finally, most commands that take filenames will optionally allow you
 to precede any filename by a commit, to specify a particular version
-fo the file:
+of the file:
 
 -------------------------------------
 $ git diff v2.5:Makefile HEAD:Makefile.in
 -------------------------------------
 
+You can also use "git cat-file -p" to see any such file:
+
+-------------------------------------
+$ git cat-file -p v2.5:Makefile
+-------------------------------------
+
 Next Steps
 ----------
 
@@ -474,6 +482,6 @@ digressions that may be interesting at this point are:
     smart enough to perform a close-to-optimal search even in the
     case of complex non-linear history with lots of merged branches.
 
-  * link:everyday.html[Everday GIT with 20 Commands Or So]
+  * link:everyday.html[Everyday GIT with 20 Commands Or So]
 
   * link:cvs-migration.html[git for CVS users].
index b6fce39..004c216 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -154,8 +154,7 @@ PROGRAMS = \
        git-hash-object$X git-index-pack$X git-local-fetch$X \
        git-mailinfo$X git-merge-base$X \
        git-merge-index$X git-mktag$X git-mktree$X git-pack-objects$X git-patch-id$X \
-       git-peek-remote$X git-prune-packed$X \
-       git-receive-pack$X git-rev-parse$X \
+       git-peek-remote$X git-prune-packed$X git-receive-pack$X \
        git-send-pack$X git-shell$X \
        git-show-index$X git-ssh-fetch$X \
        git-ssh-upload$X git-unpack-file$X \
@@ -168,7 +167,7 @@ PROGRAMS = \
 BUILT_INS = git-log$X git-whatchanged$X git-show$X \
        git-count-objects$X git-diff$X git-push$X \
        git-grep$X git-add$X git-rm$X git-rev-list$X \
-       git-check-ref-format$X \
+       git-check-ref-format$X git-rev-parse$X \
        git-init-db$X git-tar-tree$X git-upload-tar$X git-format-patch$X \
        git-ls-files$X git-ls-tree$X \
        git-read-tree$X git-commit-tree$X \
@@ -222,7 +221,7 @@ LIB_OBJS = \
 BUILTIN_OBJS = \
        builtin-log.o builtin-help.o builtin-count.o builtin-diff.o builtin-push.o \
        builtin-grep.o builtin-add.o builtin-rev-list.o builtin-check-ref-format.o \
-       builtin-rm.o builtin-init-db.o \
+       builtin-rm.o builtin-init-db.o builtin-rev-parse.o \
        builtin-tar-tree.o builtin-upload-tar.o \
        builtin-ls-files.o builtin-ls-tree.o \
        builtin-read-tree.o builtin-commit-tree.o \
index db1912a..6612f4c 100644 (file)
@@ -85,6 +85,23 @@ static int istitlechar(char c)
                (c >= '0' && c <= '9') || c == '.' || c == '_';
 }
 
+static char *extra_headers = NULL;
+static int extra_headers_size = 0;
+
+static int git_format_config(const char *var, const char *value)
+{
+       if (!strcmp(var, "format.headers")) {
+               int len = strlen(value);
+               extra_headers_size += len + 1;
+               extra_headers = realloc(extra_headers, extra_headers_size);
+               extra_headers[extra_headers_size - len - 1] = 0;
+               strcat(extra_headers, value);
+               return 0;
+       }
+       return git_default_config(var, value);
+}
+
+
 static FILE *realstdout = NULL;
 static char *output_directory = NULL;
 
@@ -150,6 +167,7 @@ int cmd_format_patch(int argc, const char **argv, char **envp)
        int numbered = 0;
        int start_number = -1;
        int keep_subject = 0;
+       char *add_signoff = NULL;
 
        init_revisions(&rev);
        rev.commit_format = CMIT_FMT_EMAIL;
@@ -161,6 +179,9 @@ int cmd_format_patch(int argc, const char **argv, char **envp)
        rev.ignore_merges = 1;
        rev.diffopt.output_format = DIFF_FORMAT_PATCH;
 
+       git_config(git_format_config);
+       rev.extra_headers = extra_headers;
+
        /*
         * Parse the arguments before setup_revisions(), or something
         * like "git fmt-patch -o a123 HEAD^.." may fail; a123 is
@@ -179,11 +200,13 @@ int cmd_format_patch(int argc, const char **argv, char **envp)
                        if (i == argc)
                                die("Need a number for --start-number");
                        start_number = strtol(argv[i], NULL, 10);
-               } else if (!strcmp(argv[i], "-k") ||
+               }
+               else if (!strcmp(argv[i], "-k") ||
                                !strcmp(argv[i], "--keep-subject")) {
                        keep_subject = 1;
                        rev.total = -1;
-               } else if (!strcmp(argv[i], "-o")) {
+               }
+               else if (!strcmp(argv[i], "-o")) {
                        if (argc < 3)
                                die ("Which directory?");
                        if (mkdir(argv[i + 1], 0777) < 0 && errno != EEXIST)
@@ -192,6 +215,16 @@ int cmd_format_patch(int argc, const char **argv, char **envp)
                        output_directory = strdup(argv[i + 1]);
                        i++;
                }
+               else if (!strcmp(argv[i], "--signoff") ||
+                        !strcmp(argv[i], "-s")) {
+                       const char *committer = git_committer_info(1);
+                       const char *endpos = strchr(committer, '>');
+                       if (!endpos)
+                               die("bogos committer info %s\n", committer);
+                       add_signoff = xmalloc(endpos - committer + 2);
+                       memcpy(add_signoff, committer, endpos - committer + 1);
+                       add_signoff[endpos - committer + 1] = 0;
+               }
                else if (!strcmp(argv[i], "--attach"))
                        rev.mime_boundary = git_version_string;
                else if (!strncmp(argv[i], "--attach=", 9))
@@ -230,6 +263,7 @@ int cmd_format_patch(int argc, const char **argv, char **envp)
        total = nr;
        if (numbered)
                rev.total = total + start_number - 1;
+       rev.add_signoff = add_signoff;
        while (0 <= --nr) {
                int shown;
                commit = list[nr];
index 10afd46..085e11e 100644 (file)
@@ -791,25 +791,26 @@ static int oneway_merge(struct cache_entry **src)
 
 static int read_cache_unmerged(void)
 {
-       int i, deleted;
+       int i;
        struct cache_entry **dst;
+       struct cache_entry *last = NULL;
 
        read_cache();
        dst = active_cache;
-       deleted = 0;
        for (i = 0; i < active_nr; i++) {
                struct cache_entry *ce = active_cache[i];
                if (ce_stage(ce)) {
-                       deleted++;
+                       if (last && !strcmp(ce->name, last->name))
+                               continue;
                        invalidate_ce_path(ce);
-                       continue;
+                       last = ce;
+                       ce->ce_mode = 0;
+                       ce->ce_flags &= ~htons(CE_STAGEMASK);
                }
-               if (deleted)
-                       *dst = ce;
-               dst++;
+               *dst++ = ce;
        }
-       active_nr -= deleted;
-       return deleted;
+       active_nr = dst - active_cache;
+       return !!last;
 }
 
 static void prime_cache_tree_rec(struct cache_tree *it, struct tree *tree)
@@ -894,7 +895,10 @@ int cmd_read_tree(int argc, const char **argv, char **envp)
                        continue;
                }
 
-               /* This differs from "-m" in that we'll silently ignore unmerged entries */
+               /* This differs from "-m" in that we'll silently ignore
+                * unmerged entries and overwrite working tree files that
+                * correspond to them.
+                */
                if (!strcmp(arg, "--reset")) {
                        if (stage || merge)
                                usage(read_tree_usage);
similarity index 98%
rename from rev-parse.c
rename to builtin-rev-parse.c
index 4e2d9fb..c353a48 100644 (file)
@@ -7,6 +7,7 @@
 #include "commit.h"
 #include "refs.h"
 #include "quote.h"
+#include "builtin.h"
 
 #define DO_REVS                1
 #define DO_NOREV       2
@@ -163,7 +164,7 @@ static int show_file(const char *arg)
        return 0;
 }
 
-int main(int argc, char **argv)
+int cmd_rev_parse(int argc, const char **argv, char **envp)
 {
        int i, as_is = 0, verify = 0;
        unsigned char sha1[20];
index 738ec3d..ffa9340 100644 (file)
--- a/builtin.h
+++ b/builtin.h
@@ -43,5 +43,6 @@ extern int cmd_diff_index(int argc, const char **argv, char **envp);
 extern int cmd_diff_stages(int argc, const char **argv, char **envp);
 extern int cmd_diff_tree(int argc, const char **argv, char **envp);
 extern int cmd_cat_file(int argc, const char **argv, char **envp);
+extern int cmd_rev_parse(int argc, const char **argv, char **envp);
 
 #endif
diff --git a/cache.h b/cache.h
index f11d5e7..d530af9 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -179,6 +179,7 @@ extern void rollback_index_file(struct cache_file *);
 extern int trust_executable_bit;
 extern int assume_unchanged;
 extern int prefer_symlink_refs;
+extern int log_all_ref_updates;
 extern int warn_ambiguous_refs;
 extern int diff_rename_limit_default;
 extern int shared_repository;
index 0248c6d..2ae6153 100644 (file)
--- a/config.c
+++ b/config.c
@@ -269,6 +269,11 @@ int git_default_config(const char *var, const char *value)
                return 0;
        }
 
+       if (!strcmp(var, "core.logallrefupdates")) {
+               log_all_ref_updates = git_config_bool(var, value);
+               return 0;
+       }
+
        if (!strcmp(var, "core.warnambiguousrefs")) {
                warn_ambiguous_refs = git_config_bool(var, value);
                return 0;
index b3e0684..aac8779 100755 (executable)
@@ -567,7 +567,6 @@ sub precommit_check {
 sub svn_checkout_tree {
        my ($svn_rev, $treeish) = @_;
        my $from = file_to_s("$REV_DIR/$svn_rev");
-       assert_svn_wc_clean($svn_rev);
        assert_tree($from);
        print "diff-tree $from $treeish\n";
        my $pid = open my $diff_fh, '-|';
index 6fa7889..23a5a2a 100644 (file)
@@ -20,9 +20,10 @@ a_empty_cr=
 a_empty_crlf=
 
 cd import
-       cat >> kw.c <<''
+       cat >> kw.c <<\EOF
 /* Make it look like somebody copied a file from CVS into SVN: */
 /* $Id: kw.c,v 1.1.1.1 1994/03/06 00:00:00 eric Exp $ */
+EOF
 
        printf "Hello\r\nWorld\r\n" > crlf
        a_crlf=`git-hash-object -w crlf`
index 444c99e..2e79eab 100644 (file)
@@ -14,6 +14,7 @@ char git_default_name[MAX_GITNAME];
 int trust_executable_bit = 1;
 int assume_unchanged = 0;
 int prefer_symlink_refs = 0;
+int log_all_ref_updates = 0;
 int warn_ambiguous_refs = 1;
 int repository_format_version = 0;
 char git_commit_encoding[MAX_ENCODING_LENGTH] = "utf-8";
index 44bb2f2..c1539d1 100644 (file)
@@ -21,7 +21,7 @@ const char *git_exec_path(void)
                return current_exec_path;
 
        env = getenv("GIT_EXEC_PATH");
-       if (env) {
+       if (env && *env) {
                return env;
        }
 
@@ -32,22 +32,25 @@ const char *git_exec_path(void)
 int execv_git_cmd(const char **argv)
 {
        char git_command[PATH_MAX + 1];
-       int len,  i;
+       int i;
        const char *paths[] = { current_exec_path,
                                getenv("GIT_EXEC_PATH"),
                                builtin_exec_path };
 
        for (i = 0; i < ARRAY_SIZE(paths); ++i) {
+               size_t len;
+               int rc;
                const char *exec_dir = paths[i];
                const char *tmp;
 
-               if (!exec_dir) continue;
+               if (!exec_dir || !*exec_dir) continue;
 
                if (*exec_dir != '/') {
                        if (!getcwd(git_command, sizeof(git_command))) {
                                fprintf(stderr, "git: cannot determine "
-                                       "current directory\n");
-                               exit(1);
+                                       "current directory: %s\n",
+                                       strerror(errno));
+                               break;
                        }
                        len = strlen(git_command);
 
@@ -57,17 +60,28 @@ int execv_git_cmd(const char **argv)
                                while (*exec_dir == '/')
                                        exec_dir++;
                        }
-                       snprintf(git_command + len, sizeof(git_command) - len,
-                                "/%s", exec_dir);
+
+                       rc = snprintf(git_command + len,
+                                     sizeof(git_command) - len, "/%s",
+                                     exec_dir);
+                       if (rc < 0 || rc >= sizeof(git_command) - len) {
+                               fprintf(stderr, "git: command name given "
+                                       "is too long.\n");
+                               break;
+                       }
                } else {
+                       if (strlen(exec_dir) + 1 > sizeof(git_command)) {
+                               fprintf(stderr, "git: command name given "
+                                       "is too long.\n");
+                               break;
+                       }
                        strcpy(git_command, exec_dir);
                }
 
                len = strlen(git_command);
-               len += snprintf(git_command + len, sizeof(git_command) - len,
-                               "/git-%s", argv[0]);
-
-               if (sizeof(git_command) <= len) {
+               rc = snprintf(git_command + len, sizeof(git_command) - len,
+                             "/git-%s", argv[0]);
+               if (rc < 0 || rc >= sizeof(git_command) - len) {
                        fprintf(stderr,
                                "git: command name given is too long.\n");
                        break;
diff --git a/fetch.c b/fetch.c
index 107504b..e040ef9 100644 (file)
--- a/fetch.c
+++ b/fetch.c
@@ -9,6 +9,7 @@
 #include "refs.h"
 
 const char *write_ref = NULL;
+const char *write_ref_log_details = NULL;
 
 int get_tree = 0;
 int get_history = 0;
@@ -215,23 +216,51 @@ static int mark_complete(const char *path, const unsigned char *sha1)
 
 int pull(char *target)
 {
+       struct ref_lock *lock = NULL;
        unsigned char sha1[20];
+       char *msg;
+       int ret;
 
        save_commit_buffer = 0;
        track_object_refs = 0;
+       if (write_ref) {
+               lock = lock_ref_sha1(write_ref, NULL, 0);
+               if (!lock) {
+                       error("Can't lock ref %s", write_ref);
+                       return -1;
+               }
+       }
 
        if (!get_recover)
                for_each_ref(mark_complete);
 
-       if (interpret_target(target, sha1))
-               return error("Could not interpret %s as something to pull",
-                            target);
-       if (process(lookup_unknown_object(sha1)))
+       if (interpret_target(target, sha1)) {
+               error("Could not interpret %s as something to pull", target);
+               if (lock)
+                       unlock_ref(lock);
                return -1;
-       if (loop())
+       }
+       if (process(lookup_unknown_object(sha1))) {
+               if (lock)
+                       unlock_ref(lock);
                return -1;
-       
-       if (write_ref)
-               write_ref_sha1_unlocked(write_ref, sha1);
+       }
+       if (loop()) {
+               if (lock)
+                       unlock_ref(lock);
+               return -1;
+       }
+
+       if (write_ref) {
+               if (write_ref_log_details) {
+                       msg = xmalloc(strlen(write_ref_log_details) + 12);
+                       sprintf(msg, "fetch from %s", write_ref_log_details);
+               } else
+                       msg = NULL;
+               ret = write_ref_sha1(lock, sha1, msg ? msg : "fetch (unknown)");
+               if (msg)
+                       free(msg);
+               return ret;
+       }
        return 0;
 }
diff --git a/fetch.h b/fetch.h
index 001a6b8..841bb1a 100644 (file)
--- a/fetch.h
+++ b/fetch.h
@@ -25,6 +25,9 @@ extern int fetch_ref(char *ref, unsigned char *sha1);
 /* If set, the ref filename to write the target value to. */
 extern const char *write_ref;
 
+/* If set additional text will appear in the ref log. */
+extern const char *write_ref_log_details;
+
 /* Set to fetch the target tree. */
 extern int get_tree;
 
index 97ec2d0..4232e27 100755 (executable)
--- a/git-am.sh
+++ b/git-am.sh
@@ -413,7 +413,7 @@ do
        parent=$(git-rev-parse --verify HEAD) &&
        commit=$(git-commit-tree $tree -p $parent <"$dotest/final-commit") &&
        echo Committed: $commit &&
-       git-update-ref HEAD $commit $parent ||
+       git-update-ref -m "am: $SUBJECT" HEAD $commit $parent ||
        stop_here $this
 
        if test -x "$GIT_DIR"/hooks/post-applypatch
index 12cab1e..e4b0947 100755 (executable)
@@ -204,7 +204,7 @@ echo Wrote tree $tree
 parent=$(git-rev-parse --verify HEAD) &&
 commit=$(git-commit-tree $tree -p $parent <"$final") || exit 1
 echo Committed: $commit
-git-update-ref HEAD $commit $parent || exit
+git-update-ref -m "applypatch: $SUBJECT" HEAD $commit $parent || exit
 
 if test -x "$GIT_DIR"/hooks/post-applypatch
 then
index 134e68c..e0501ec 100755 (executable)
@@ -1,6 +1,6 @@
 #!/bin/sh
 
-USAGE='[(-d | -D) <branchname>] | [[-f] <branchname> [<start-point>]] | -r'
+USAGE='[-l] [(-d | -D) <branchname>] | [[-f] <branchname> [<start-point>]] | -r'
 LONG_USAGE='If no arguments, show available branches and mark current branch with a star.
 If one argument, create a new branch <branchname> based off of current HEAD.
 If two arguments, create a new branch <branchname> based off of <start-point>.'
@@ -42,6 +42,7 @@ If you are sure you want to delete it, run 'git branch -D $branch_name'."
            esac
            ;;
        esac
+       rm -f "$GIT_DIR/logs/refs/heads/$branch_name"
        rm -f "$GIT_DIR/refs/heads/$branch_name"
        echo "Deleted branch $branch_name."
     done
@@ -55,6 +56,7 @@ ls_remote_branches () {
 }
 
 force=
+create_log=
 while case "$#,$1" in 0,*) break ;; *,-*) ;; *) break ;; esac
 do
        case "$1" in
@@ -69,6 +71,9 @@ do
        -f)
                force="$1"
                ;;
+       -l)
+               create_log="yes"
+               ;;
        --)
                shift
                break
@@ -117,4 +122,9 @@ then
                die "cannot force-update the current branch."
        fi
 fi
-git update-ref "refs/heads/$branchname" $rev
+if test "$create_log" = 'yes'
+then
+       mkdir -p $(dirname "$GIT_DIR/logs/refs/heads/$branchname")
+       touch "$GIT_DIR/logs/refs/heads/$branchname"
+fi
+git update-ref -m "branch: Created from $head" "refs/heads/$branchname" $rev
index a11c939..564117f 100755 (executable)
@@ -5,10 +5,13 @@ SUBDIRECTORY_OK=Sometimes
 . git-sh-setup
 
 old=$(git-rev-parse HEAD)
+old_name=HEAD
 new=
+new_name=
 force=
 branch=
 newbranch=
+newbranch_log=
 merge=
 while [ "$#" != "0" ]; do
     arg="$1"
@@ -24,6 +27,9 @@ while [ "$#" != "0" ]; do
                git-check-ref-format "heads/$newbranch" ||
                        die "git checkout: we do not like '$newbranch' as a branch name."
                ;;
+       "-l")
+               newbranch_log=1
+               ;;
        "-f")
                force=1
                ;;
@@ -44,6 +50,7 @@ while [ "$#" != "0" ]; do
                                exit 1
                        fi
                        new="$rev"
+                       new_name="$arg^0"
                        if [ -f "$GIT_DIR/refs/heads/$arg" ]; then
                                branch="$arg"
                        fi
@@ -51,9 +58,11 @@ while [ "$#" != "0" ]; do
                then
                        # checking out selected paths from a tree-ish.
                        new="$rev"
+                       new_name="$arg^{tree}"
                        branch=
                else
                        new=
+                       new_name=
                        branch=
                        set x "$arg" "$@"
                        shift
@@ -114,7 +123,7 @@ then
        cd "$cdup"
 fi
 
-[ -z "$new" ] && new=$old
+[ -z "$new" ] && new=$old && new_name="$old_name"
 
 # If we don't have an old branch that we're switching to,
 # and we don't have a new branch name for the target we
@@ -187,9 +196,11 @@ fi
 #
 if [ "$?" -eq 0 ]; then
        if [ "$newbranch" ]; then
-               leading=`expr "refs/heads/$newbranch" : '\(.*\)/'` &&
-               mkdir -p "$GIT_DIR/$leading" &&
-               echo $new >"$GIT_DIR/refs/heads/$newbranch" || exit
+               if [ "$newbranch_log" ]; then
+                       mkdir -p $(dirname "$GIT_DIR/logs/refs/heads/$newbranch")
+                       touch "$GIT_DIR/logs/refs/heads/$newbranch"
+               fi
+               git-update-ref -m "checkout: Created from $new_name" "refs/heads/$newbranch" $new || exit
                branch="$newbranch"
        fi
        [ "$branch" ] &&
index bb56264..3834323 100755 (executable)
@@ -19,8 +19,8 @@ ignored=
 ignoredonly=
 cleandir=
 quiet=
-rmf="rm -f"
-rmrf="rm -rf"
+rmf="rm -f --"
+rmrf="rm -rf --"
 rm_refuse="echo Not removing"
 echo1="echo"
 
index 1983d45..6dd04fd 100755 (executable)
@@ -260,20 +260,41 @@ do
   -m|--m|--me|--mes|--mess|--messa|--messag|--message)
       case "$#" in 1) usage ;; esac
       shift
-      log_given=t$log_given
-      log_message="$1"
+      log_given=m$log_given
+      if test "$log_message" = ''
+      then
+          log_message="$1"
+      else
+          log_message="$log_message
+
+$1"
+      fi
       no_edit=t
       shift
       ;;
   -m*)
-      log_given=t$log_given
-      log_message=`expr "$1" : '-m\(.*\)'`
+      log_given=m$log_given
+      if test "$log_message" = ''
+      then
+          log_message=`expr "$1" : '-m\(.*\)'`
+      else
+          log_message="$log_message
+
+`expr "$1" : '-m\(.*\)'`"
+      fi
       no_edit=t
       shift
       ;;
   --m=*|--me=*|--mes=*|--mess=*|--messa=*|--messag=*|--message=*)
-      log_given=t$log_given
-      log_message=`expr "$1" : '-[^=]*=\(.*\)'`
+      log_given=m$log_given
+      if test "$log_message" = ''
+      then
+          log_message=`expr "$1" : '-[^=]*=\(.*\)'`
+      else
+          log_message="$log_message
+
+`expr "$1" : '-[^=]*=\(.*\)'`"
+      fi
       no_edit=t
       shift
       ;;
@@ -378,7 +399,9 @@ esac
 
 case "$log_given" in
 tt*)
-  die "Only one of -c/-C/-F/-m can be used." ;;
+  die "Only one of -c/-C/-F can be used." ;;
+*tm*|*mt*)
+  die "Option -m cannot be combined with -c/-C/-F." ;;
 esac
 
 case "$#,$also,$only,$amend" in
@@ -690,7 +713,8 @@ then
                rm -f "$TMP_INDEX"
        fi &&
        commit=$(cat "$GIT_DIR"/COMMIT_MSG | git-commit-tree $tree $PARENTS) &&
-       git-update-ref HEAD $commit $current &&
+       rlogm=$(sed -e 1q "$GIT_DIR"/COMMIT_MSG) &&
+       git-update-ref -m "commit: $rlogm" HEAD $commit $current &&
        rm -f -- "$GIT_DIR/MERGE_HEAD" &&
        if test -f "$NEXT_INDEX"
        then
index 69bd810..48818f8 100755 (executable)
@@ -166,7 +166,10 @@ fast_forward_local () {
            mb=$(git-merge-base "$local" "$2") &&
            case "$2,$mb" in
            $local,*)
-               echo >&2 "* $1: same as $3"
+               if test -n "$verbose"
+               then
+                       echo >&2 "* $1: same as $3"
+               fi
                ;;
            *,$local)
                echo >&2 "* $1: fast forward to $3"
index 0ee3e3e..296f3b7 100755 (executable)
@@ -48,7 +48,7 @@ then
 else
        rm -f "$GIT_DIR/ORIG_HEAD"
 fi
-git-update-ref HEAD "$rev"
+git-update-ref -m "reset $reset_type $@" HEAD "$rev"
 
 case "$reset_type" in
 --hard )
index 312a4ea..ed1d89b 100755 (executable)
@@ -21,7 +21,6 @@ use warnings;
 use Term::ReadLine;
 use Getopt::Long;
 use Data::Dumper;
-use Net::SMTP;
 
 # most mail servers generate the Date: header, but not all...
 $ENV{LC_ALL} = 'C';
@@ -37,7 +36,8 @@ sub cleanup_compose_files();
 my $compose_filename = ".msg.$$";
 
 # Variables we fill in automatically, or via prompting:
-my (@to,@cc,@initial_cc,$initial_reply_to,$initial_subject,@files,$from,$compose,$time);
+my (@to,@cc,@initial_cc,@bcclist,
+       $initial_reply_to,$initial_subject,@files,$from,$compose,$time);
 
 # Behavior modification variables
 my ($chain_reply_to, $quiet, $suppress_from, $no_signed_off_cc) = (1, 0, 0, 0);
@@ -56,6 +56,7 @@ my $rc = GetOptions("from=s" => \$from,
                    "subject=s" => \$initial_subject,
                    "to=s" => \@to,
                    "cc=s" => \@initial_cc,
+                   "bcc=s" => \@bcclist,
                    "chain-reply-to!" => \$chain_reply_to,
                    "smtp-server=s" => \$smtp_server,
                    "compose" => \$compose,
@@ -160,6 +161,7 @@ sub expand_aliases {
 
 @to = expand_aliases(@to);
 @initial_cc = expand_aliases(@initial_cc);
+@bcclist = expand_aliases(@bcclist);
 
 if (!defined $initial_subject && $compose) {
        do {
@@ -269,6 +271,9 @@ Options:
    --cc           Specify an initial "Cc:" list for the entire series
                   of emails.
 
+   --bcc          Specify a list of email addresses that should be Bcc:
+                 on all the emails.
+
    --compose      Use \$EDITOR to edit an introductory message for the
                   patch series.
 
@@ -303,7 +308,7 @@ EOT
 }
 
 # Variables we set as part of the loop over files
-our ($message_id, $cc, %mail, $subject, $reply_to, $message);
+our ($message_id, $cc, %mail, $subject, $reply_to, $references, $message);
 
 sub extract_valid_address {
        my $address = shift;
@@ -316,7 +321,11 @@ sub extract_valid_address {
        } else {
                # less robust/correct than the monster regexp in Email::Valid,
                # but still does a 99% job, and one less dependency
-               return ($address =~ /([^\"<>\s]+@[^<>\s]+)/);
+               my $cleaned_address;
+               if ($address =~ /([^\"<>\s]+@[^<>\s]+)/) {
+                       $cleaned_address = $1;
+               }
+               return $cleaned_address;
        }
 }
 
@@ -348,7 +357,7 @@ sub send_message
 {
        my @recipients = unique_email_list(@to);
        my $to = join (",\n\t", @recipients);
-       @recipients = unique_email_list(@recipients,@cc);
+       @recipients = unique_email_list(@recipients,@cc,@bcclist);
        my $date = strftime('%a, %d %b %Y %H:%M:%S %z', localtime($time++));
        my $gitversion = '@@GIT_VERSION@@';
        if ($gitversion =~ m/..GIT_VERSION../) {
@@ -367,17 +376,24 @@ Date: $date
 Message-Id: $message_id
 X-Mailer: git-send-email $gitversion
 ";
-       $header .= "In-Reply-To: $reply_to\n" if $reply_to;
+       if ($reply_to) {
+
+               $header .= "In-Reply-To: $reply_to\n";
+               $header .= "References: $references\n";
+       }
 
        if ($smtp_server =~ m#^/#) {
                my $pid = open my $sm, '|-';
                defined $pid or die $!;
                if (!$pid) {
-                       exec($smtp_server,'-i',@recipients) or die $!;
+                       exec($smtp_server,'-i',
+                            map { scalar extract_valid_address($_) }
+                            @recipients) or die $!;
                }
                print $sm "$header\n$message";
                close $sm or die $?;
        } else {
+               require Net::SMTP;
                $smtp ||= Net::SMTP->new( $smtp_server );
                $smtp->mail( $from ) or die $smtp->message;
                $smtp->to( @recipients ) or die $smtp->message;
@@ -406,6 +422,7 @@ X-Mailer: git-send-email $gitversion
 }
 
 $reply_to = $initial_reply_to;
+$references = $initial_reply_to || '';
 make_message_id();
 $subject = $initial_subject;
 
@@ -482,6 +499,11 @@ foreach my $t (@files) {
        # set up for the next message
        if ($chain_reply_to || length($reply_to) == 0) {
                $reply_to = $message_id;
+               if (length $references > 0) {
+                       $references .= " $message_id";
+               } else {
+                       $references = "$message_id";
+               }
        }
        make_message_id();
 }
index 61f559f..38ac732 100755 (executable)
@@ -63,10 +63,17 @@ my $svn_dir = $ARGV[1];
 
 our @mergerx = ();
 if ($opt_m) {
-       @mergerx = ( qr/\W(?:from|of|merge|merging|merged) (\w+)/i );
+       my $branch_esc = quotemeta ($branch_name);
+       my $trunk_esc  = quotemeta ($trunk_name);
+       @mergerx =
+       (
+               qr!\b(?:merg(?:ed?|ing))\b.*?\b((?:(?<=$branch_esc/)[\w\.\-]+)|(?:$trunk_esc))\b!i,
+               qr!\b(?:from|of)\W+((?:(?<=$branch_esc/)[\w\.\-]+)|(?:$trunk_esc))\b!i,
+               qr!\b(?:from|of)\W+(?:the )?([\w\.\-]+)[-\s]branch\b!i
+       );
 }
 if ($opt_M) {
-       push (@mergerx, qr/$opt_M/);
+       unshift (@mergerx, qr/$opt_M/);
 }
 
 # Absolutize filename now, since we will have chdir'ed by the time we
diff --git a/git.c b/git.c
index 10ea934..bc463c9 100644 (file)
--- a/git.c
+++ b/git.c
@@ -69,7 +69,8 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
                { "diff-index", cmd_diff_index },
                { "diff-stages", cmd_diff_stages },
                { "diff-tree", cmd_diff_tree },
-               { "cat-file", cmd_cat_file }
+               { "cat-file", cmd_cat_file },
+               { "rev-parse", cmd_rev_parse }
        };
        int i;
 
diff --git a/gitk b/gitk
index 4aa57c0..101cf9b 100755 (executable)
--- a/gitk
+++ b/gitk
@@ -34,10 +34,10 @@ proc start_rev_list {view} {
        set order "--date-order"
     }
     if {[catch {
-       set fd [open [concat | git-rev-list --header $order \
+       set fd [open [concat | git rev-list --header $order \
                          --parents --boundary --default HEAD $args] r]
     } err]} {
-       puts stderr "Error executing git-rev-list: $err"
+       puts stderr "Error executing git rev-list: $err"
        exit 1
     }
     set commfd($view) $fd
@@ -94,10 +94,10 @@ proc getcommitlines {fd view}  {
            }
            if {[string range $err 0 4] == "usage"} {
                set err "Gitk: error reading commits$fv:\
-                       bad arguments to git-rev-list."
+                       bad arguments to git rev-list."
                if {$viewname($view) eq "Command line"} {
                    append err \
-                       "  (Note: arguments to gitk are passed to git-rev-list\
+                       "  (Note: arguments to gitk are passed to git rev-list\
                         to allow selection of commits to be displayed.)"
                }
            } else {
@@ -148,7 +148,7 @@ proc getcommitlines {fd view}  {
            if {[string length $shortcmit] > 80} {
                set shortcmit "[string range $shortcmit 0 80]..."
            }
-           error_popup "Can't parse git-rev-list output: {$shortcmit}"
+           error_popup "Can't parse git rev-list output: {$shortcmit}"
            exit 1
        }
        set id [lindex $ids 0]
@@ -217,7 +217,7 @@ proc doupdate {} {
 }
 
 proc readcommit {id} {
-    if {[catch {set contents [exec git-cat-file commit $id]}]} return
+    if {[catch {set contents [exec git cat-file commit $id]}]} return
     parsecommit $id $contents 0
 }
 
@@ -276,8 +276,8 @@ proc parsecommit {id contents listed} {
        set headline $comment
     }
     if {!$listed} {
-       # git-rev-list indents the comment by 4 spaces;
-       # if we got this via git-cat-file, add the indentation
+       # git rev-list indents the comment by 4 spaces;
+       # if we got this via git cat-file, add the indentation
        set newcomment {}
        foreach line [split $comment "\n"] {
            append newcomment "    "
@@ -337,14 +337,14 @@ proc readrefs {} {
            set type {}
            set tag {}
            catch {
-               set commit [exec git-rev-parse "$id^0"]
+               set commit [exec git rev-parse "$id^0"]
                if {"$commit" != "$id"} {
                    set tagids($name) $commit
                    lappend idtags($commit) $name
                }
            }           
            catch {
-               set tagcontents($name) [exec git-cat-file tag "$id"]
+               set tagcontents($name) [exec git cat-file tag "$id"]
            }
        } elseif { $type == "heads" } {
            set headids($name) $id
@@ -357,21 +357,21 @@ proc readrefs {} {
     close $refd
 }
 
-proc show_error {w msg} {
+proc show_error {w top msg} {
     message $w.m -text $msg -justify center -aspect 400
     pack $w.m -side top -fill x -padx 20 -pady 20
-    button $w.ok -text OK -command "destroy $w"
+    button $w.ok -text OK -command "destroy $top"
     pack $w.ok -side bottom -fill x
-    bind $w <Visibility> "grab $w; focus $w"
-    bind $w <Key-Return> "destroy $w"
-    tkwait window $w
+    bind $top <Visibility> "grab $top; focus $top"
+    bind $top <Key-Return> "destroy $top"
+    tkwait window $top
 }
 
 proc error_popup msg {
     set w .error
     toplevel $w
     wm transient $w .
-    show_error $w $msg
+    show_error $w $w $msg
 }
 
 proc makewindow {} {
@@ -380,7 +380,7 @@ proc makewindow {} {
     global findtype findtypemenu findloc findstring fstring geometry
     global entries sha1entry sha1string sha1but
     global maincursor textcursor curtextcursor
-    global rowctxmenu mergemax
+    global rowctxmenu mergemax wrapcomment
 
     menu .bar
     .bar add cascade -label "File" -menu .bar.file
@@ -527,6 +527,7 @@ proc makewindow {} {
     pack $ctext -side left -fill both -expand 1
     .ctop.cdet add .ctop.cdet.left
 
+    $ctext tag conf comment -wrap $wrapcomment
     $ctext tag conf filesep -font [concat $textfont bold] -back "#aaaaaa"
     $ctext tag conf hunksep -fore blue
     $ctext tag conf d0 -fore red
@@ -696,7 +697,7 @@ proc savestuff {w} {
     global stuffsaved findmergefiles maxgraphpct
     global maxwidth
     global viewname viewfiles viewargs viewperm nextviewnum
-    global cmitmode
+    global cmitmode wrapcomment
 
     if {$stuffsaved} return
     if {![winfo viewable .]} return
@@ -709,6 +710,7 @@ proc savestuff {w} {
        puts $f [list set maxgraphpct $maxgraphpct]
        puts $f [list set maxwidth $maxwidth]
        puts $f [list set cmitmode $cmitmode]
+       puts $f [list set wrapcomment $wrapcomment]
        puts $f "set geometry(width) [winfo width .ctop]"
        puts $f "set geometry(height) [winfo height .ctop]"
        puts $f "set geometry(canv1) [expr {[winfo width $canv]-2}]"
@@ -1285,7 +1287,7 @@ proc vieweditor {top n title} {
     checkbutton $top.perm -text "Remember this view" -variable newviewperm($n)
     grid $top.perm - -pady 5 -sticky w
     message $top.al -aspect 1000 -font $uifont \
-       -text "Commits to include (arguments to git-rev-list):"
+       -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
@@ -2939,7 +2941,7 @@ proc findpatches {} {
     }
 
     if {[catch {
-       set f [open [list | git-diff-tree --stdin -s -r -S$findstring \
+       set f [open [list | git diff-tree --stdin -s -r -S$findstring \
                         << $inputids] r]
     } err]} {
        error_popup "Error starting search process: $err"
@@ -2971,7 +2973,7 @@ proc readfindproc {} {
        return
     }
     if {![regexp {^[0-9a-f]{40}} $line id]} {
-       error_popup "Can't parse git-diff-tree output: $line"
+       error_popup "Can't parse git diff-tree output: $line"
        stopfindproc
        return
     }
@@ -3036,10 +3038,10 @@ proc findfiles {} {
        if {$l == $findstartline} break
     }
 
-    # start off a git-diff-tree process if needed
+    # start off a git diff-tree process if needed
     if {$diffsneeded ne {}} {
        if {[catch {
-           set df [open [list | git-diff-tree -r --stdin << $diffsneeded] r]
+           set df [open [list | git diff-tree -r --stdin << $diffsneeded] r]
        } err ]} {
            error_popup "Error starting search process: $err"
            return
@@ -3069,7 +3071,7 @@ proc readfilediffs {df} {
            if {[catch {close $df} err]} {
                stopfindproc
                bell
-               error_popup "Error in git-diff-tree: $err"
+               error_popup "Error in git diff-tree: $err"
            } elseif {[info exists findid]} {
                set id $findid
                stopfindproc
@@ -3096,7 +3098,7 @@ proc donefilediff {} {
     if {[info exists fdiffid]} {
        while {[lindex $fdiffsneeded $fdiffpos] ne $fdiffid
               && $fdiffpos < [llength $fdiffsneeded]} {
-           # git-diff-tree doesn't output anything for a commit
+           # git diff-tree doesn't output anything for a commit
            # which doesn't change anything
            set nullid [lindex $fdiffsneeded $fdiffpos]
            set treediffs($nullid) {}
@@ -3213,8 +3215,11 @@ proc selcanvline {w x y} {
 
 proc commit_descriptor {p} {
     global commitinfo
+    if {![info exists commitinfo($p)]} {
+       getcommit $p
+    }
     set l "..."
-    if {[info exists commitinfo($p)]} {
+    if {[llength $commitinfo($p)] > 1} {
        set l [lindex $commitinfo($p) 0]
     }
     return "$p ($l)"
@@ -3222,11 +3227,11 @@ proc commit_descriptor {p} {
 
 # append some text to the ctext widget, and make any SHA1 ID
 # that we know about be a clickable link.
-proc appendwithlinks {text} {
+proc appendwithlinks {text tags} {
     global ctext commitrow linknum curview
 
     set start [$ctext index "end - 1c"]
-    $ctext insert end $text
+    $ctext insert end $text $tags
     $ctext insert end "\n"
     set links [regexp -indices -all -inline {[0-9a-f]{40}} $text]
     foreach l $links {
@@ -3354,7 +3359,7 @@ proc selectline {l isnew} {
        $ctext insert end "\n"
     }
  
-    set comment {}
+    set headers {}
     set olds [lindex $parentlist $l]
     if {[llength $olds] > 1} {
        set np 0
@@ -3365,23 +3370,22 @@ proc selectline {l isnew} {
                set tag m$np
            }
            $ctext insert end "Parent: " $tag
-           appendwithlinks [commit_descriptor $p]
+           appendwithlinks [commit_descriptor $p] {}
            incr np
        }
     } else {
        foreach p $olds {
-           append comment "Parent: [commit_descriptor $p]\n"
+           append headers "Parent: [commit_descriptor $p]\n"
        }
     }
 
     foreach c [lindex $childlist $l] {
-       append comment "Child:  [commit_descriptor $c]\n"
+       append headers "Child:  [commit_descriptor $c]\n"
     }
-    append comment "\n"
-    append comment [lindex $info 5]
 
     # make anything that looks like a SHA1 ID be a clickable link
-    appendwithlinks $comment
+    appendwithlinks $headers {}
+    appendwithlinks [lindex $info 5] {comment}
 
     $ctext tag delete Comments
     $ctext tag remove found 1.0 end
@@ -3426,6 +3430,7 @@ proc selnextpage {dir} {
        set lpp 1
     }
     allcanvs yview scroll [expr {$dir * $lpp}] units
+    drawvisible
     if {![info exists selectedline]} return
     set l [expr {$selectedline + $dir * $lpp}]
     if {$l < 0} {
@@ -3521,7 +3526,7 @@ proc gettree {id} {
     catch {unset diffmergeid}
     if {![info exists treefilelist($id)]} {
        if {![info exists treepending]} {
-           if {[catch {set gtf [open [concat | git-ls-tree -r $id] r]}]} {
+           if {[catch {set gtf [open [concat | git ls-tree -r $id] r]}]} {
                return
            }
            set treepending $id
@@ -3569,7 +3574,7 @@ proc showfile {f} {
        return
     }
     set blob [lindex $treeidlist($diffids) $i]
-    if {[catch {set bf [open [concat | git-cat-file blob $blob] r]} err]} {
+    if {[catch {set bf [open [concat | git cat-file blob $blob] r]} err]} {
        puts "oops, error reading blob $blob: $err"
        return
     }
@@ -3611,7 +3616,7 @@ proc mergediff {id l} {
     set diffids $id
     # this doesn't seem to actually affect anything...
     set env(GIT_DIFF_OPTS) $diffopts
-    set cmd [concat | git-diff-tree --no-commit-id --cc $id]
+    set cmd [concat | git diff-tree --no-commit-id --cc $id]
     if {[catch {set mdf [open $cmd r]} err]} {
        error_popup "Error getting merge diffs: $err"
        return
@@ -3723,7 +3728,7 @@ proc gettreediffs {ids} {
     set treepending $ids
     set treediff {}
     if {[catch \
-        {set gdtf [open [concat | git-diff-tree --no-commit-id -r $ids] r]} \
+        {set gdtf [open [concat | git diff-tree --no-commit-id -r $ids] r]} \
        ]} return
     fconfigure $gdtf -blocking 0
     fileevent $gdtf readable [list gettreediffline $gdtf $ids]
@@ -3759,7 +3764,7 @@ proc getblobdiffs {ids} {
     global nextupdate diffinhdr treediffs
 
     set env(GIT_DIFF_OPTS) $diffopts
-    set cmd [concat | git-diff-tree --no-commit-id -r -p -C $ids]
+    set cmd [concat | git diff-tree --no-commit-id -r -p -C $ids]
     if {[catch {set bdf [open $cmd r]} err]} {
        puts "error getting diffs: $err"
        return
@@ -4296,7 +4301,7 @@ proc mkpatchgo {} {
     set oldid [$patchtop.fromsha1 get]
     set newid [$patchtop.tosha1 get]
     set fname [$patchtop.fname get]
-    if {[catch {exec git-diff-tree -p $oldid $newid >$fname &} err]} {
+    if {[catch {exec git diff-tree -p $oldid $newid >$fname &} err]} {
        error_popup "Error creating patch: $err"
     }
     catch {destroy $patchtop}
@@ -4504,7 +4509,7 @@ proc showtag {tag isnew} {
     } else {
        set text "Tag: $tag\nId:  $tagids($tag)"
     }
-    appendwithlinks $text
+    appendwithlinks $text {}
     $ctext conf -state disabled
     init_flist {}
 }
@@ -4863,11 +4868,11 @@ proc tcl_encoding {enc} {
 # defaults...
 set datemode 0
 set diffopts "-U 5 -p"
-set wrcomcmd "git-diff-tree --stdin -p --pretty"
+set wrcomcmd "git diff-tree --stdin -p --pretty"
 
 set gitencoding {}
 catch {
-    set gitencoding [exec git-repo-config --get i18n.commitencoding]
+    set gitencoding [exec git repo-config --get i18n.commitencoding]
 }
 if {$gitencoding == ""} {
     set gitencoding "utf-8"
@@ -4890,6 +4895,7 @@ set downarrowlen 7
 set mingaplen 30
 set flistmode "flat"
 set cmitmode "patch"
+set wrapcomment "none"
 
 set colors {green red blue magenta darkgrey brown orange}
 
@@ -4911,7 +4917,7 @@ foreach arg $argv {
 # check that we can find a .git directory somewhere...
 set gitdir [gitdir]
 if {![file isdirectory $gitdir]} {
-    show_error . "Cannot find the git directory \"$gitdir\"."
+    show_error {} . "Cannot find the git directory \"$gitdir\"."
     exit 1
 }
 
@@ -4922,7 +4928,7 @@ if {$i >= 0} {
     set revtreeargs [lrange $revtreeargs 0 [expr {$i - 1}]]
 } elseif {$revtreeargs ne {}} {
     if {[catch {
-       set f [eval exec git-rev-parse --no-revs --no-flags $revtreeargs]
+       set f [eval exec git rev-parse --no-revs --no-flags $revtreeargs]
        set cmdline_files [split $f "\n"]
        set n [llength $cmdline_files]
        set revtreeargs [lrange $revtreeargs 0 end-$n]
@@ -4931,9 +4937,9 @@ if {$i >= 0} {
        # so look for "fatal:".
        set i [string first "fatal:" $err]
        if {$i > 0} {
-           set err [string range [expr {$i + 6}] end]
+           set err [string range $err [expr {$i + 6}] end]
        }
-       show_error . "Bad arguments to gitk:\n$err"
+       show_error {} . "Bad arguments to gitk:\n$err"
        exit 1
     }
 }
index 861644b..661c909 100644 (file)
@@ -1223,6 +1223,7 @@ int main(int argc, char **argv)
        int rc = 0;
 
        setup_git_directory();
+       git_config(git_default_config);
 
        while (arg < argc && argv[arg][0] == '-') {
                if (argv[arg][1] == 't') {
@@ -1249,6 +1250,7 @@ int main(int argc, char **argv)
        }
        commit_id = argv[arg];
        url = argv[arg + 1];
+       write_ref_log_details = url;
 
        http_init();
 
@@ -1269,10 +1271,10 @@ int main(int argc, char **argv)
        if (pull(commit_id))
                rc = 1;
 
-       curl_slist_free_all(no_pragma_header);
-
        http_cleanup();
 
+       curl_slist_free_all(no_pragma_header);
+
        if (corrupt_object_found) {
                fprintf(stderr,
 "Some loose object were found to be corrupt, but they might be just\n"
diff --git a/http.c b/http.c
index 0cb42a8..146cf7b 100644 (file)
--- a/http.c
+++ b/http.c
@@ -25,7 +25,6 @@ long curl_low_speed_limit = -1;
 long curl_low_speed_time = -1;
 
 struct curl_slist *pragma_header;
-struct curl_slist *no_range_header;
 
 struct active_request_slot *active_queue_head = NULL;
 
@@ -208,7 +207,6 @@ void http_init(void)
        curl_global_init(CURL_GLOBAL_ALL);
 
        pragma_header = curl_slist_append(pragma_header, "Pragma: no-cache");
-       no_range_header = curl_slist_append(no_range_header, "Range:");
 
 #ifdef USE_CURL_MULTI
        {
@@ -344,9 +342,14 @@ struct active_request_slot *get_active_slot(void)
        slot->finished = NULL;
        slot->callback_data = NULL;
        slot->callback_func = NULL;
+       curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, NULL);
        curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, pragma_header);
-       curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, no_range_header);
        curl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, curl_errorstr);
+       curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, NULL);
+       curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, NULL);
+       curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, NULL);
+       curl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 0);
+       curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1);
 
        return slot;
 }
index fa9e697..ffa4887 100644 (file)
@@ -208,6 +208,7 @@ int main(int argc, char **argv)
        int arg = 1;
 
        setup_git_directory();
+       git_config(git_default_config);
 
        while (arg < argc && argv[arg][0] == '-') {
                if (argv[arg][1] == 't')
@@ -239,6 +240,7 @@ int main(int argc, char **argv)
                usage(local_pull_usage);
        commit_id = argv[arg];
        path = argv[arg + 1];
+       write_ref_log_details = path;
 
        if (pull(commit_id))
                return 1;
index 58b0163..ebb49f2 100644 (file)
@@ -12,6 +12,37 @@ static void show_parents(struct commit *commit, int abbrev)
        }
 }
 
+static int append_signoff(char *buf, int buf_sz, int at, const char *signoff)
+{
+       int signoff_len = strlen(signoff);
+       static const char signed_off_by[] = "Signed-off-by: ";
+       char *cp = buf;
+
+       /* Do we have enough space to add it? */
+       if (buf_sz - at <= strlen(signed_off_by) + signoff_len + 2)
+               return at;
+
+       /* First see if we already have the sign-off by the signer */
+       while (1) {
+               cp = strstr(cp, signed_off_by);
+               if (!cp)
+                       break;
+               cp += strlen(signed_off_by);
+               if ((cp + signoff_len < buf + at) &&
+                   !strncmp(cp, signoff, signoff_len) &&
+                   isspace(cp[signoff_len]))
+                       return at; /* we already have him */
+       }
+
+       strcpy(buf + at, signed_off_by);
+       at += strlen(signed_off_by);
+       strcpy(buf + at, signoff);
+       at += signoff_len;
+       buf[at++] = '\n';
+       buf[at] = 0;
+       return at;
+}
+
 void show_log(struct rev_info *opt, struct log_info *log, const char *sep)
 {
        static char this_header[16384];
@@ -20,7 +51,7 @@ void show_log(struct rev_info *opt, struct log_info *log, const char *sep)
        int abbrev_commit = opt->abbrev_commit ? opt->abbrev : 40;
        const char *extra;
        int len;
-       char *subject = NULL, *after_subject = NULL;
+       const char *subject = NULL, *extra_headers = opt->extra_headers;
 
        opt->loginfo = NULL;
        if (!opt->verbose_header) {
@@ -69,6 +100,7 @@ void show_log(struct rev_info *opt, struct log_info *log, const char *sep)
                        static char subject_buffer[1024];
                        static char buffer[1024];
                        snprintf(subject_buffer, sizeof(subject_buffer) - 1,
+                                "%s"
                                 "MIME-Version: 1.0\n"
                                 "Content-Type: multipart/mixed;\n"
                                 " boundary=\"%s%s\"\n"
@@ -79,9 +111,10 @@ void show_log(struct rev_info *opt, struct log_info *log, const char *sep)
                                 "Content-Type: text/plain; "
                                 "charset=UTF-8; format=fixed\n"
                                 "Content-Transfer-Encoding: 8bit\n\n",
+                                extra_headers ? extra_headers : "",
                                 mime_boundary_leader, opt->mime_boundary,
                                 mime_boundary_leader, opt->mime_boundary);
-                       after_subject = subject_buffer;
+                       extra_headers = subject_buffer;
 
                        snprintf(buffer, sizeof(buffer) - 1,
                                 "--%s%s\n"
@@ -110,7 +143,11 @@ void show_log(struct rev_info *opt, struct log_info *log, const char *sep)
        /*
         * And then the pretty-printed message itself
         */
-       len = pretty_print_commit(opt->commit_format, commit, ~0u, this_header, sizeof(this_header), abbrev, subject, after_subject);
+       len = pretty_print_commit(opt->commit_format, commit, ~0u, this_header, sizeof(this_header), abbrev, subject, extra_headers);
+
+       if (opt->add_signoff)
+               len = append_signoff(this_header, sizeof(this_header), len,
+                                    opt->add_signoff);
        printf("%s%s%s", this_header, extra, sep);
 }
 
index c529e2d..70a569c 100644 (file)
@@ -162,7 +162,7 @@ int main(int argc, const char **argv)
 
        while (*argp) {
                const char *file = *argp++;
-               FILE *f = !strcmp(file, "-") ? stdin : fopen(file, "rt");
+               FILE *f = !strcmp(file, "-") ? stdin : fopen(file, "r");
                int file_done = 0;
 
                if ( !f )
diff --git a/refs.c b/refs.c
index 0f3491f..eeb1196 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -142,6 +142,8 @@ static int do_for_each_ref(const char *base, int (*fn)(const char *path, const u
                        namelen = strlen(de->d_name);
                        if (namelen > 255)
                                continue;
+                       if (namelen>5 && !strcmp(de->d_name+namelen-5,".lock"))
+                               continue;
                        memcpy(path + baselen, de->d_name, namelen+1);
                        if (stat(git_path("%s", path), &st) < 0)
                                continue;
@@ -198,26 +200,6 @@ int for_each_remote_ref(int (*fn)(const char *path, const unsigned char *sha1))
        return do_for_each_ref("refs/remotes", fn, 13);
 }
 
-static char *ref_file_name(const char *ref)
-{
-       char *base = get_refs_directory();
-       int baselen = strlen(base);
-       int reflen = strlen(ref);
-       char *ret = xmalloc(baselen + 2 + reflen);
-       sprintf(ret, "%s/%s", base, ref);
-       return ret;
-}
-
-static char *ref_lock_file_name(const char *ref)
-{
-       char *base = get_refs_directory();
-       int baselen = strlen(base);
-       int reflen = strlen(ref);
-       char *ret = xmalloc(baselen + 7 + reflen);
-       sprintf(ret, "%s/%s.lock", base, ref);
-       return ret;
-}
-
 int get_ref_sha1(const char *ref, unsigned char *sha1)
 {
        if (check_ref_format(ref))
@@ -225,94 +207,6 @@ int get_ref_sha1(const char *ref, unsigned char *sha1)
        return read_ref(git_path("refs/%s", ref), sha1);
 }
 
-static int lock_ref_file(const char *filename, const char *lock_filename,
-                        const unsigned char *old_sha1)
-{
-       int fd = open(lock_filename, O_WRONLY | O_CREAT | O_EXCL, 0666);
-       unsigned char current_sha1[20];
-       int retval;
-       if (fd < 0) {
-               return error("Couldn't open lock file for %s: %s",
-                            filename, strerror(errno));
-       }
-       retval = read_ref(filename, current_sha1);
-       if (old_sha1) {
-               if (retval) {
-                       close(fd);
-                       unlink(lock_filename);
-                       return error("Could not read the current value of %s",
-                                    filename);
-               }
-               if (memcmp(current_sha1, old_sha1, 20)) {
-                       close(fd);
-                       unlink(lock_filename);
-                       error("The current value of %s is %s",
-                             filename, sha1_to_hex(current_sha1));
-                       return error("Expected %s",
-                                    sha1_to_hex(old_sha1));
-               }
-       } else {
-               if (!retval) {
-                       close(fd);
-                       unlink(lock_filename);
-                       return error("Unexpectedly found a value of %s for %s",
-                                    sha1_to_hex(current_sha1), filename);
-               }
-       }
-       return fd;
-}
-
-int lock_ref_sha1(const char *ref, const unsigned char *old_sha1)
-{
-       char *filename;
-       char *lock_filename;
-       int retval;
-       if (check_ref_format(ref))
-               return -1;
-       filename = ref_file_name(ref);
-       lock_filename = ref_lock_file_name(ref);
-       retval = lock_ref_file(filename, lock_filename, old_sha1);
-       free(filename);
-       free(lock_filename);
-       return retval;
-}
-
-static int write_ref_file(const char *filename,
-                         const char *lock_filename, int fd,
-                         const unsigned char *sha1)
-{
-       char *hex = sha1_to_hex(sha1);
-       char term = '\n';
-       if (write(fd, hex, 40) < 40 ||
-           write(fd, &term, 1) < 1) {
-               error("Couldn't write %s", filename);
-               close(fd);
-               return -1;
-       }
-       close(fd);
-       rename(lock_filename, filename);
-       return 0;
-}
-
-int write_ref_sha1(const char *ref, int fd, const unsigned char *sha1)
-{
-       char *filename;
-       char *lock_filename;
-       int retval;
-       if (fd < 0)
-               return -1;
-       if (check_ref_format(ref))
-               return -1;
-       filename = ref_file_name(ref);
-       lock_filename = ref_lock_file_name(ref);
-       if (safe_create_leading_directories(filename))
-               die("unable to create leading directory for %s", filename);
-       retval = write_ref_file(filename, lock_filename, fd, sha1);
-       free(filename);
-       free(lock_filename);
-       return retval;
-}
-
 /*
  * Make sure "ref" is something reasonable to have under ".git/refs/";
  * We do not like it if:
@@ -365,25 +259,255 @@ int check_ref_format(const char *ref)
        }
 }
 
-int write_ref_sha1_unlocked(const char *ref, const unsigned char *sha1)
+static struct ref_lock* verify_lock(struct ref_lock *lock,
+       const unsigned char *old_sha1, int mustexist)
+{
+       char buf[40];
+       int nr, fd = open(lock->ref_file, O_RDONLY);
+       if (fd < 0 && (mustexist || errno != ENOENT)) {
+               error("Can't verify ref %s", lock->ref_file);
+               unlock_ref(lock);
+               return NULL;
+       }
+       nr = read(fd, buf, 40);
+       close(fd);
+       if (nr != 40 || get_sha1_hex(buf, lock->old_sha1) < 0) {
+               error("Can't verify ref %s", lock->ref_file);
+               unlock_ref(lock);
+               return NULL;
+       }
+       if (memcmp(lock->old_sha1, old_sha1, 20)) {
+               error("Ref %s is at %s but expected %s", lock->ref_file,
+                       sha1_to_hex(lock->old_sha1), sha1_to_hex(old_sha1));
+               unlock_ref(lock);
+               return NULL;
+       }
+       return lock;
+}
+
+static struct ref_lock* lock_ref_sha1_basic(const char *path,
+       int plen,
+       const unsigned char *old_sha1, int mustexist)
+{
+       struct ref_lock *lock;
+       struct stat st;
+
+       lock = xcalloc(1, sizeof(struct ref_lock));
+       lock->lock_fd = -1;
+
+       plen = strlen(path) - plen;
+       path = resolve_ref(path, lock->old_sha1, mustexist);
+       if (!path) {
+               unlock_ref(lock);
+               return NULL;
+       }
+
+       lock->ref_file = strdup(path);
+       lock->lock_file = strdup(mkpath("%s.lock", lock->ref_file));
+       lock->log_file = strdup(git_path("logs/%s", lock->ref_file + plen));
+       lock->force_write = lstat(lock->ref_file, &st) && errno == ENOENT;
+
+       if (safe_create_leading_directories(lock->lock_file))
+               die("unable to create directory for %s", lock->lock_file);
+       lock->lock_fd = open(lock->lock_file,
+               O_WRONLY | O_CREAT | O_EXCL, 0666);
+       if (lock->lock_fd < 0) {
+               error("Couldn't open lock file %s: %s",
+                       lock->lock_file, strerror(errno));
+               unlock_ref(lock);
+               return NULL;
+       }
+
+       return old_sha1 ? verify_lock(lock, old_sha1, mustexist) : lock;
+}
+
+struct ref_lock* lock_ref_sha1(const char *ref,
+       const unsigned char *old_sha1, int mustexist)
 {
-       char *filename;
-       char *lock_filename;
-       int fd;
-       int retval;
        if (check_ref_format(ref))
+               return NULL;
+       return lock_ref_sha1_basic(git_path("refs/%s", ref),
+               5 + strlen(ref), old_sha1, mustexist);
+}
+
+struct ref_lock* lock_any_ref_for_update(const char *ref,
+       const unsigned char *old_sha1, int mustexist)
+{
+       return lock_ref_sha1_basic(git_path("%s", ref),
+               strlen(ref), old_sha1, mustexist);
+}
+
+void unlock_ref (struct ref_lock *lock)
+{
+       if (lock->lock_fd >= 0) {
+               close(lock->lock_fd);
+               unlink(lock->lock_file);
+       }
+       if (lock->ref_file)
+               free(lock->ref_file);
+       if (lock->lock_file)
+               free(lock->lock_file);
+       if (lock->log_file)
+               free(lock->log_file);
+       free(lock);
+}
+
+static int log_ref_write(struct ref_lock *lock,
+       const unsigned char *sha1, const char *msg)
+{
+       int logfd, written, oflags = O_APPEND | O_WRONLY;
+       unsigned maxlen, len;
+       char *logrec;
+       const char *comitter;
+
+       if (log_all_ref_updates) {
+               if (safe_create_leading_directories(lock->log_file) < 0)
+                       return error("unable to create directory for %s",
+                               lock->log_file);
+               oflags |= O_CREAT;
+       }
+
+       logfd = open(lock->log_file, oflags, 0666);
+       if (logfd < 0) {
+               if (!log_all_ref_updates && errno == ENOENT)
+                       return 0;
+               return error("Unable to append to %s: %s",
+                       lock->log_file, strerror(errno));
+       }
+
+       setup_ident();
+       comitter = git_committer_info(1);
+       if (msg) {
+               maxlen = strlen(comitter) + strlen(msg) + 2*40 + 5;
+               logrec = xmalloc(maxlen);
+               len = snprintf(logrec, maxlen, "%s %s %s\t%s\n",
+                       sha1_to_hex(lock->old_sha1),
+                       sha1_to_hex(sha1),
+                       comitter,
+                       msg);
+       } else {
+               maxlen = strlen(comitter) + 2*40 + 4;
+               logrec = xmalloc(maxlen);
+               len = snprintf(logrec, maxlen, "%s %s %s\n",
+                       sha1_to_hex(lock->old_sha1),
+                       sha1_to_hex(sha1),
+                       comitter);
+       }
+       written = len <= maxlen ? write(logfd, logrec, len) : -1;
+       free(logrec);
+       close(logfd);
+       if (written != len)
+               return error("Unable to append to %s", lock->log_file);
+       return 0;
+}
+
+int write_ref_sha1(struct ref_lock *lock,
+       const unsigned char *sha1, const char *logmsg)
+{
+       static char term = '\n';
+
+       if (!lock)
                return -1;
-       filename = ref_file_name(ref);
-       lock_filename = ref_lock_file_name(ref);
-       if (safe_create_leading_directories(filename))
-               die("unable to create leading directory for %s", filename);
-       fd = open(lock_filename, O_WRONLY | O_CREAT | O_EXCL, 0666);
-       if (fd < 0) {
-               error("Writing %s", lock_filename);
-               perror("Open");
+       if (!lock->force_write && !memcmp(lock->old_sha1, sha1, 20)) {
+               unlock_ref(lock);
+               return 0;
        }
-       retval = write_ref_file(filename, lock_filename, fd, sha1);
-       free(filename);
-       free(lock_filename);
-       return retval;
+       if (write(lock->lock_fd, sha1_to_hex(sha1), 40) != 40 ||
+           write(lock->lock_fd, &term, 1) != 1
+               || close(lock->lock_fd) < 0) {
+               error("Couldn't write %s", lock->lock_file);
+               unlock_ref(lock);
+               return -1;
+       }
+       if (log_ref_write(lock, sha1, logmsg) < 0) {
+               unlock_ref(lock);
+               return -1;
+       }
+       if (rename(lock->lock_file, lock->ref_file) < 0) {
+               error("Couldn't set %s", lock->ref_file);
+               unlock_ref(lock);
+               return -1;
+       }
+       lock->lock_fd = -1;
+       unlock_ref(lock);
+       return 0;
+}
+
+int read_ref_at(const char *ref, unsigned long at_time, unsigned char *sha1)
+{
+       const char *logfile, *logdata, *logend, *rec, *lastgt, *lastrec;
+       char *tz_c;
+       int logfd, tz;
+       struct stat st;
+       unsigned long date;
+       unsigned char logged_sha1[20];
+
+       logfile = git_path("logs/%s", ref);
+       logfd = open(logfile, O_RDONLY, 0);
+       if (logfd < 0)
+               die("Unable to read log %s: %s", logfile, strerror(errno));
+       fstat(logfd, &st);
+       if (!st.st_size)
+               die("Log %s is empty.", logfile);
+       logdata = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, logfd, 0);
+       close(logfd);
+
+       lastrec = NULL;
+       rec = logend = logdata + st.st_size;
+       while (logdata < rec) {
+               if (logdata < rec && *(rec-1) == '\n')
+                       rec--;
+               lastgt = NULL;
+               while (logdata < rec && *(rec-1) != '\n') {
+                       rec--;
+                       if (*rec == '>')
+                               lastgt = rec;
+               }
+               if (!lastgt)
+                       die("Log %s is corrupt.", logfile);
+               date = strtoul(lastgt + 1, &tz_c, 10);
+               if (date <= at_time) {
+                       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 (memcmp(logged_sha1, sha1, 20)) {
+                                       tz = strtoul(tz_c, NULL, 10);
+                                       fprintf(stderr,
+                                               "warning: Log %s has gap after %s.\n",
+                                               logfile, show_rfc2822_date(date, tz));
+                               }
+                       } else if (date == at_time) {
+                               if (get_sha1_hex(rec + 41, sha1))
+                                       die("Log %s is corrupt.", logfile);
+                       } else {
+                               if (get_sha1_hex(rec + 41, logged_sha1))
+                                       die("Log %s is corrupt.", logfile);
+                               if (memcmp(logged_sha1, sha1, 20)) {
+                                       tz = strtoul(tz_c, NULL, 10);
+                                       fprintf(stderr,
+                                               "warning: Log %s unexpectedly ended on %s.\n",
+                                               logfile, show_rfc2822_date(date, tz));
+                               }
+                       }
+                       munmap((void*)logdata, st.st_size);
+                       return 0;
+               }
+               lastrec = rec;
+       }
+
+       rec = logdata;
+       while (rec < logend && *rec != '>' && *rec != '\n')
+               rec++;
+       if (rec == logend || *rec == '\n')
+               die("Log %s is corrupt.", logfile);
+       date = strtoul(rec + 1, &tz_c, 10);
+       tz = strtoul(tz_c, NULL, 10);
+       if (get_sha1_hex(logdata, sha1))
+               die("Log %s is corrupt.", logfile);
+       munmap((void*)logdata, st.st_size);
+       fprintf(stderr, "warning: Log %s only goes back to %s.\n",
+               logfile, show_rfc2822_date(date, tz));
+       return 0;
 }
diff --git a/refs.h b/refs.h
index fa816c1..6c946ea 100644 (file)
--- a/refs.h
+++ b/refs.h
@@ -1,6 +1,15 @@
 #ifndef REFS_H
 #define REFS_H
 
+struct ref_lock {
+       char *ref_file;
+       char *lock_file;
+       char *log_file;
+       unsigned char old_sha1[20];
+       int lock_fd;
+       int force_write;
+};
+
 /*
  * Calls the specified function for each ref file until it returns nonzero,
  * and returns the value
@@ -14,16 +23,20 @@ extern int for_each_remote_ref(int (*fn)(const char *path, const unsigned char *
 /** Reads the refs file specified into sha1 **/
 extern int get_ref_sha1(const char *ref, unsigned char *sha1);
 
-/** Locks ref and returns the fd to give to write_ref_sha1() if the ref
- * has the given value currently; otherwise, returns -1.
- **/
-extern int lock_ref_sha1(const char *ref, const unsigned char *old_sha1);
+/** Locks a "refs/" ref returning the lock on success and NULL on failure. **/
+extern struct ref_lock* lock_ref_sha1(const char *ref, const unsigned char *old_sha1, int mustexist);
+
+/** Locks any ref (for 'HEAD' type refs). */
+extern struct ref_lock* lock_any_ref_for_update(const char *ref, const unsigned char *old_sha1, int mustexist);
+
+/** Release any lock taken but not written. **/
+extern void unlock_ref (struct ref_lock *lock);
 
-/** Writes sha1 into the refs file specified, locked with the given fd. **/
-extern int write_ref_sha1(const char *ref, int fd, const unsigned char *sha1);
+/** Writes sha1 into the ref specified by the lock. **/
+extern int write_ref_sha1(struct ref_lock *lock, const unsigned char *sha1, const char *msg);
 
-/** Writes sha1 into the refs file specified. **/
-extern int write_ref_sha1_unlocked(const char *ref, const unsigned char *sha1);
+/** Reads log for the value of ref during at_time. **/
+extern int read_ref_at(const char *ref, unsigned long at_time, unsigned char *sha1);
 
 /** Returns 0 if target has the right format for a ref. **/
 extern int check_ref_format(const char *target);
index bdbdd23..7d85b0f 100644 (file)
@@ -60,6 +60,8 @@ struct rev_info {
        struct log_info *loginfo;
        int             nr, total;
        const char      *mime_boundary;
+       const char      *add_signoff;
+       const char      *extra_headers;
 
        /* special limits */
        int max_count;
index f77c189..aea0f40 100644 (file)
@@ -617,6 +617,12 @@ static void prepare_packed_git_one(char *objdir, int local)
 
                /* we have .idx.  Is it a file we can map? */
                strcpy(path + len, de->d_name);
+               for (p = packed_git; p; p = p->next) {
+                       if (!memcmp(path, p->pack_name, len + namelen - 4))
+                               break;
+               }
+               if (p)
+                       continue;
                p = add_packed_git(path, len + namelen, local);
                if (!p)
                        continue;
@@ -626,12 +632,12 @@ static void prepare_packed_git_one(char *objdir, int local)
        closedir(dir);
 }
 
+static int prepare_packed_git_run_once = 0;
 void prepare_packed_git(void)
 {
-       static int run_once = 0;
        struct alternate_object_database *alt;
 
-       if (run_once)
+       if (prepare_packed_git_run_once)
                return;
        prepare_packed_git_one(get_object_directory(), 1);
        prepare_alt_odb();
@@ -640,7 +646,13 @@ void prepare_packed_git(void)
                prepare_packed_git_one(alt->base, 0);
                alt->name[-1] = '/';
        }
-       run_once = 1;
+       prepare_packed_git_run_once = 1;
+}
+
+static void reprepare_packed_git(void)
+{
+       prepare_packed_git_run_once = 0;
+       prepare_packed_git();
 }
 
 int check_sha1_signature(const unsigned char *sha1, void *map, unsigned long size, const char *type)
@@ -1212,9 +1224,12 @@ int sha1_object_info(const unsigned char *sha1, char *type, unsigned long *sizep
        if (!map) {
                struct pack_entry e;
 
-               if (!find_pack_entry(sha1, &e))
-                       return error("unable to find %s", sha1_to_hex(sha1));
-               return packed_object_info(&e, type, sizep);
+               if (find_pack_entry(sha1, &e))
+                       return packed_object_info(&e, type, sizep);
+               reprepare_packed_git();
+               if (find_pack_entry(sha1, &e))
+                       return packed_object_info(&e, type, sizep);
+               return error("unable to find %s", sha1_to_hex(sha1));
        }
        if (unpack_sha1_header(&stream, map, mapsize, hdr, sizeof(hdr)) < 0)
                status = error("unable to unpack %s header",
@@ -1256,6 +1271,9 @@ void * read_sha1_file(const unsigned char *sha1, char *type, unsigned long *size
                munmap(map, mapsize);
                return buf;
        }
+       reprepare_packed_git();
+       if (find_pack_entry(sha1, &e))
+               return read_packed_sha1(sha1, type, size);
        return NULL;
 }
 
index dc68355..fbbde1c 100644 (file)
@@ -4,6 +4,7 @@
 #include "tree.h"
 #include "blob.h"
 #include "tree-walk.h"
+#include "refs.h"
 
 static int find_short_object_filename(int len, const char *name, unsigned char *sha1)
 {
@@ -245,36 +246,61 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
                "refs/remotes/%.*s/HEAD",
                NULL
        };
-       const char **p;
-       const char *warning = "warning: refname '%.*s' is ambiguous.\n";
-       char *pathname;
-       int already_found = 0;
+       static const char *warning = "warning: refname '%.*s' is ambiguous.\n";
+       const char **p, *pathname;
+       char *real_path = NULL;
+       int refs_found = 0, am;
+       unsigned long at_time = (unsigned long)-1;
        unsigned char *this_result;
        unsigned char sha1_from_ref[20];
 
        if (len == 40 && !get_sha1_hex(str, sha1))
                return 0;
 
+       /* At a given period of time? "@{2 hours ago}" */
+       for (am = 1; am < len - 1; am++) {
+               if (str[am] == '@' && str[am+1] == '{' && str[len-1] == '}') {
+                       int date_len = len - am - 3;
+                       char *date_spec = xmalloc(date_len + 1);
+                       strncpy(date_spec, str + am + 2, date_len);
+                       date_spec[date_len] = 0;
+                       at_time = approxidate(date_spec);
+                       free(date_spec);
+                       len = am;
+                       break;
+               }
+       }
+
        /* Accept only unambiguous ref paths. */
        if (ambiguous_path(str, len))
                return -1;
 
        for (p = fmt; *p; p++) {
-               this_result = already_found ? sha1_from_ref : sha1;
-               pathname = git_path(*p, len, str);
-               if (!read_ref(pathname, this_result)) {
-                       if (warn_ambiguous_refs) {
-                               if (already_found)
-                                       fprintf(stderr, warning, len, str);
-                               already_found++;
-                       }
-                       else
-                               return 0;
+               this_result = refs_found ? sha1_from_ref : sha1;
+               pathname = resolve_ref(git_path(*p, len, str), this_result, 1);
+               if (pathname) {
+                       if (!refs_found++)
+                               real_path = strdup(pathname);
+                       if (!warn_ambiguous_refs)
+                               break;
                }
        }
-       if (already_found)
-               return 0;
-       return -1;
+
+       if (!refs_found)
+               return -1;
+
+       if (warn_ambiguous_refs && refs_found > 1)
+               fprintf(stderr, warning, len, str);
+
+       if (at_time != (unsigned long)-1) {
+               read_ref_at(
+                       real_path + strlen(git_path(".")) - 1,
+                       at_time,
+                       sha1);
+       }
+
+       free(real_path);
+       return 0;
 }
 
 static int get_sha1_1(const char *name, int len, unsigned char *sha1);
@@ -456,7 +482,7 @@ static int get_sha1_1(const char *name, int len, unsigned char *sha1)
  */
 int get_sha1(const char *name, unsigned char *sha1)
 {
-       int ret;
+       int ret, bracket_depth;
        unsigned unused;
        int namelen = strlen(name);
        const char *cp;
@@ -502,8 +528,15 @@ int get_sha1(const char *name, unsigned char *sha1)
                }
                return -1;
        }
-       cp = strchr(name, ':');
-       if (cp) {
+       for (cp = name, bracket_depth = 0; *cp; cp++) {
+               if (*cp == '{')
+                       bracket_depth++;
+               else if (bracket_depth && *cp == '}')
+                       bracket_depth--;
+               else if (!bracket_depth && *cp == ':')
+                       break;
+       }
+       if (*cp == ':') {
                unsigned char tree_sha1[20];
                if (!get_sha1_1(name, cp-name, tree_sha1))
                        return get_tree_entry(tree_sha1, cp+1, sha1,
index 4eb9e04..e3067b8 100644 (file)
@@ -132,6 +132,7 @@ int main(int argc, char **argv)
        if (!prog) prog = "git-ssh-upload";
 
        setup_git_directory();
+       git_config(git_default_config);
 
        while (arg < argc && argv[arg][0] == '-') {
                if (argv[arg][1] == 't') {
@@ -158,6 +159,7 @@ int main(int argc, char **argv)
        }
        commit_id = argv[arg];
        url = argv[arg + 1];
+       write_ref_log_details = url;
 
        if (setup_connection(&fd_in, &fd_out, prog, url, arg, argv + 1))
                return 1;
diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh
new file mode 100755 (executable)
index 0000000..df3e993
--- /dev/null
@@ -0,0 +1,213 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Shawn Pearce
+#
+
+test_description='Test git-update-ref and basic ref logging'
+. ./test-lib.sh
+
+Z=0000000000000000000000000000000000000000
+A=1111111111111111111111111111111111111111
+B=2222222222222222222222222222222222222222
+C=3333333333333333333333333333333333333333
+D=4444444444444444444444444444444444444444
+E=5555555555555555555555555555555555555555
+F=6666666666666666666666666666666666666666
+m=refs/heads/master
+
+test_expect_success \
+       "create $m" \
+       'git-update-ref $m $A &&
+        test $A = $(cat .git/$m)'
+test_expect_success \
+       "create $m" \
+       'git-update-ref $m $B $A &&
+        test $B = $(cat .git/$m)'
+rm -f .git/$m
+
+test_expect_success \
+       "create $m (by HEAD)" \
+       'git-update-ref HEAD $A &&
+        test $A = $(cat .git/$m)'
+test_expect_success \
+       "create $m (by HEAD)" \
+       'git-update-ref HEAD $B $A &&
+        test $B = $(cat .git/$m)'
+rm -f .git/$m
+
+test_expect_failure \
+       '(not) create HEAD with old sha1' \
+       'git-update-ref HEAD $A $B'
+test_expect_failure \
+       "(not) prior created .git/$m" \
+       'test -f .git/$m'
+rm -f .git/$m
+
+test_expect_success \
+       "create HEAD" \
+       'git-update-ref HEAD $A'
+test_expect_failure \
+       '(not) change HEAD with wrong SHA1' \
+       'git-update-ref HEAD $B $Z'
+test_expect_failure \
+       "(not) changed .git/$m" \
+       'test $B = $(cat .git/$m)'
+rm -f .git/$m
+
+mkdir -p .git/logs/refs/heads
+touch .git/logs/refs/heads/master
+test_expect_success \
+       "create $m (logged by touch)" \
+       'GIT_COMMITTER_DATE="2005-05-26 23:30" \
+        git-update-ref HEAD $A -m "Initial Creation" &&
+        test $A = $(cat .git/$m)'
+test_expect_success \
+       "update $m (logged by touch)" \
+       'GIT_COMMITTER_DATE="2005-05-26 23:31" \
+        git-update-ref HEAD $B $A -m "Switch" &&
+        test $B = $(cat .git/$m)'
+test_expect_success \
+       "set $m (logged by touch)" \
+       'GIT_COMMITTER_DATE="2005-05-26 23:41" \
+        git-update-ref HEAD $A &&
+        test $A = $(cat .git/$m)'
+
+cat >expect <<EOF
+$Z $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000      Initial Creation
+$A $B $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150260 +0000      Switch
+$B $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150860 +0000
+EOF
+test_expect_success \
+       "verifying $m's log" \
+       'diff expect .git/logs/$m'
+rm -rf .git/$m .git/logs expect
+
+test_expect_success \
+       'enable core.logAllRefUpdates' \
+       'git-repo-config core.logAllRefUpdates true &&
+        test true = $(git-repo-config --bool --get core.logAllRefUpdates)'
+
+test_expect_success \
+       "create $m (logged by config)" \
+       'GIT_COMMITTER_DATE="2005-05-26 23:32" \
+        git-update-ref HEAD $A -m "Initial Creation" &&
+        test $A = $(cat .git/$m)'
+test_expect_success \
+       "update $m (logged by config)" \
+       'GIT_COMMITTER_DATE="2005-05-26 23:33" \
+        git-update-ref HEAD $B $A -m "Switch" &&
+        test $B = $(cat .git/$m)'
+test_expect_success \
+       "set $m (logged by config)" \
+       'GIT_COMMITTER_DATE="2005-05-26 23:43" \
+        git-update-ref HEAD $A &&
+        test $A = $(cat .git/$m)'
+
+cat >expect <<EOF
+$Z $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150320 +0000      Initial Creation
+$A $B $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150380 +0000      Switch
+$B $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150980 +0000
+EOF
+test_expect_success \
+       "verifying $m's log" \
+       'diff expect .git/logs/$m'
+rm -f .git/$m .git/logs/$m expect
+
+git-update-ref $m $D
+cat >.git/logs/$m <<EOF
+$C $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150320 -0500
+$A $B $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150380 -0500
+$F $Z $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150680 -0500
+$Z $E $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150980 -0500
+EOF
+
+ed="Thu, 26 May 2005 18:32:00 -0500"
+gd="Thu, 26 May 2005 18:33:00 -0500"
+ld="Thu, 26 May 2005 18:43:00 -0500"
+test_expect_success \
+       'Query "master@{May 25 2005}" (before history)' \
+       '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_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)"'
+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_expect_success \
+       'Query "master@{May 26 2005 23:32:00}" (exactly history start)' \
+       'rm -f o e
+        git-rev-parse --verify "master@{May 26 2005 23:32:00}" >o 2>e &&
+        test $A = $(cat o) &&
+        test "" = "$(cat e)"'
+test_expect_success \
+       'Query "master@{2005-05-26 23:33:01}" (middle of history with gap)' \
+       'rm -f o e
+        git-rev-parse --verify "master@{2005-05-26 23:33:01}" >o 2>e &&
+        test $B = $(cat o) &&
+        test "warning: Log .git/logs/$m has gap after $gd." = "$(cat e)"'
+test_expect_success \
+       'Query "master@{2005-05-26 23:38:00}" (middle of history)' \
+       'rm -f o e
+        git-rev-parse --verify "master@{2005-05-26 23:38:00}" >o 2>e &&
+        test $Z = $(cat o) &&
+        test "" = "$(cat e)"'
+test_expect_success \
+       'Query "master@{2005-05-26 23:43:00}" (exact end of history)' \
+       'rm -f o e
+        git-rev-parse --verify "master@{2005-05-26 23:43:00}" >o 2>e &&
+        test $E = $(cat o) &&
+        test "" = "$(cat e)"'
+test_expect_success \
+       'Query "master@{2005-05-28}" (past end of history)' \
+       'rm -f o e
+        git-rev-parse --verify "master@{2005-05-28}" >o 2>e &&
+        test $D = $(cat o) &&
+        test "warning: Log .git/logs/$m unexpectedly ended on $ld." = "$(cat e)"'
+
+
+rm -f .git/$m .git/logs/$m expect
+
+test_expect_success \
+    'creating initial files' \
+    'echo TEST >F &&
+     git-add F &&
+        GIT_AUTHOR_DATE="2005-05-26 23:30" \
+        GIT_COMMITTER_DATE="2005-05-26 23:30" git-commit -m add -a &&
+        h_TEST=$(git-rev-parse --verify HEAD)
+        echo The other day this did not work. >M &&
+        echo And then Bob told me how to fix it. >>M &&
+        echo OTHER >F &&
+        GIT_AUTHOR_DATE="2005-05-26 23:41" \
+        GIT_COMMITTER_DATE="2005-05-26 23:41" git-commit -F M -a &&
+        h_OTHER=$(git-rev-parse --verify HEAD)
+        rm -f M'
+
+cat >expect <<EOF
+$Z $h_TEST $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000 commit: add
+$h_TEST $h_OTHER $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150860 +0000   commit: The other day this did not work.
+EOF
+test_expect_success \
+       'git-commit logged updates' \
+       'diff expect .git/logs/$m'
+unset h_TEST h_OTHER
+
+test_expect_success \
+       'git-cat-file blob master:F (expect OTHER)' \
+       'test OTHER = $(git-cat-file blob master:F)'
+test_expect_success \
+       'git-cat-file blob master@{2005-05-26 23:30}:F (expect TEST)' \
+       'test TEST = $(git-cat-file blob "master@{2005-05-26 23:30}:F")'
+test_expect_success \
+       'git-cat-file blob master@{2005-05-26 23:42}:F (expect OTHER)' \
+       'test OTHER = $(git-cat-file blob "master@{2005-05-26 23:42}:F")'
+
+test_done
index 77aed8d..a78ea7f 100755 (executable)
@@ -8,15 +8,16 @@ test_description='git-update-index --again test.
 
 . ./test-lib.sh
 
+cat > expected <<\EOF
+100644 3b18e512dba79e4c8300dd08aeb37f8e728b8dad 0      file1
+100644 9db8893856a8a02eaa73470054b7c1c5a7c82e47 0      file2
+EOF
 test_expect_success 'update-index --add' \
        'echo hello world >file1 &&
         echo goodbye people >file2 &&
         git-update-index --add file1 file2 &&
         git-ls-files -s >current &&
-        cmp current - <<\EOF
-100644 3b18e512dba79e4c8300dd08aeb37f8e728b8dad 0      file1
-100644 9db8893856a8a02eaa73470054b7c1c5a7c82e47 0      file2
-EOF'
+        cmp current expected'
 
 test_expect_success 'update-index --again' \
        'rm -f file1 &&
@@ -29,20 +30,22 @@ test_expect_success 'update-index --again' \
                echo happy - failed as expected
        fi &&
         git-ls-files -s >current &&
-        cmp current - <<\EOF
-100644 3b18e512dba79e4c8300dd08aeb37f8e728b8dad 0      file1
-100644 9db8893856a8a02eaa73470054b7c1c5a7c82e47 0      file2
-EOF'
+        cmp current expected'
 
+cat > expected <<\EOF
+100644 0f1ae1422c2bf43f117d3dbd715c988a9ed2103f 0      file2
+EOF
 test_expect_success 'update-index --remove --again' \
        'git-update-index --remove --again &&
         git-ls-files -s >current &&
-        cmp current - <<\EOF
-100644 0f1ae1422c2bf43f117d3dbd715c988a9ed2103f 0      file2
-EOF'
+        cmp current expected'
 
 test_expect_success 'first commit' 'git-commit -m initial'
 
+cat > expected <<\EOF
+100644 53ab446c3f4e42ce9bb728a0ccb283a101be4979 0      dir1/file3
+100644 0f1ae1422c2bf43f117d3dbd715c988a9ed2103f 0      file2
+EOF
 test_expect_success 'update-index again' \
        'mkdir -p dir1 &&
        echo hello world >dir1/file3 &&
@@ -52,11 +55,12 @@ test_expect_success 'update-index again' \
        echo happy >dir1/file3 &&
        git-update-index --again &&
        git-ls-files -s >current &&
-       cmp current - <<\EOF
-100644 53ab446c3f4e42ce9bb728a0ccb283a101be4979 0      dir1/file3
-100644 0f1ae1422c2bf43f117d3dbd715c988a9ed2103f 0      file2
-EOF'
+       cmp current expected'
 
+cat > expected <<\EOF
+100644 d7fb3f695f06c759dbf3ab00046e7cc2da22d10f 0      dir1/file3
+100644 0f1ae1422c2bf43f117d3dbd715c988a9ed2103f 0      file2
+EOF
 test_expect_success 'update-index --update from subdir' \
        'echo not so happy >file2 &&
        cd dir1 &&
@@ -64,19 +68,17 @@ test_expect_success 'update-index --update from subdir' \
        git-update-index --again &&
        cd .. &&
        git-ls-files -s >current &&
-       cmp current - <<\EOF
-100644 d7fb3f695f06c759dbf3ab00046e7cc2da22d10f 0      dir1/file3
-100644 0f1ae1422c2bf43f117d3dbd715c988a9ed2103f 0      file2
-EOF'
+       cmp current expected'
 
+cat > expected <<\EOF
+100644 594fb5bb1759d90998e2bf2a38261ae8e243c760 0      dir1/file3
+100644 0f1ae1422c2bf43f117d3dbd715c988a9ed2103f 0      file2
+EOF
 test_expect_success 'update-index --update with pathspec' \
        'echo very happy >file2 &&
        cat file2 >dir1/file3 &&
        git-update-index --again dir1/ &&
        git-ls-files -s >current &&
-       cmp current - <<\EOF
-100644 594fb5bb1759d90998e2bf2a38261ae8e243c760 0      dir1/file3
-100644 0f1ae1422c2bf43f117d3dbd715c988a9ed2103f 0      file2
-EOF'
+       cmp current expected'
 
 test_done
index c3de151..5b04efc 100755 (executable)
@@ -14,7 +14,8 @@ test_expect_success \
     'prepare an trivial repository' \
     'echo Hello > A &&
      git-update-index --add A &&
-     git-commit -m "Initial commit."'
+     git-commit -m "Initial commit." &&
+     HEAD=$(git-rev-parse --verify HEAD)'
 
 test_expect_success \
     'git branch --help should return success now.' \
@@ -32,4 +33,32 @@ test_expect_success \
     'git branch a/b/c should create a branch' \
     'git-branch a/b/c && test -f .git/refs/heads/a/b/c'
 
+cat >expect <<EOF
+0000000000000000000000000000000000000000 $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000     branch: Created from HEAD
+EOF
+test_expect_success \
+    'git branch -l d/e/f should create a branch and a log' \
+       'GIT_COMMITTER_DATE="2005-05-26 23:30" \
+     git-branch -l d/e/f &&
+        test -f .git/refs/heads/d/e/f &&
+        test -f .git/logs/refs/heads/d/e/f &&
+        diff expect .git/logs/refs/heads/d/e/f'
+
+test_expect_success \
+    'git branch -d d/e/f should delete a branch and a log' \
+       'git-branch -d d/e/f &&
+        test ! -f .git/refs/heads/d/e/f &&
+        test ! -f .git/logs/refs/heads/d/e/f'
+
+cat >expect <<EOF
+0000000000000000000000000000000000000000 $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000     checkout: Created from master^0
+EOF
+test_expect_success \
+    'git checkout -b g/h/i -l should create a branch and a log' \
+       'GIT_COMMITTER_DATE="2005-05-26 23:30" \
+     git-checkout -b g/h/i -l master &&
+        test -f .git/refs/heads/g/h/i &&
+        test -f .git/logs/refs/heads/g/h/i &&
+        diff expect .git/logs/refs/heads/g/h/i'
+
 test_done
index 72a93da..c12270e 100755 (executable)
@@ -40,9 +40,11 @@ test_expect_success 'git-ls-files no-funny' \
 t0=`git-write-tree`
 echo "$t0" >t0
 
-echo 'just space
+cat > expected <<\EOF
+just space
 no-funny
-"tabs\t,\" (dq) and spaces"' >expected
+"tabs\t,\" (dq) and spaces"
+EOF
 test_expect_success 'git-ls-files with-funny' \
        'git-update-index --add "$p1" &&
        git-ls-files >current &&
@@ -58,14 +60,18 @@ test_expect_success 'git-ls-files -z with-funny' \
 t1=`git-write-tree`
 echo "$t1" >t1
 
-echo 'just space
+cat > expected <<\EOF
+just space
 no-funny
-"tabs\t,\" (dq) and spaces"' >expected
+"tabs\t,\" (dq) and spaces"
+EOF
 test_expect_success 'git-ls-tree with funny' \
        'git-ls-tree -r $t1 | sed -e "s/^[^     ]*      //" >current &&
         diff -u expected current'
 
-echo 'A        "tabs\t,\" (dq) and spaces"' >expected
+cat > expected <<\EOF
+A      "tabs\t,\" (dq) and spaces"
+EOF
 test_expect_success 'git-diff-index with-funny' \
        'git-diff-index --name-status $t0 >current &&
        diff -u expected current'
@@ -84,53 +90,62 @@ test_expect_success 'git-diff-tree -z with-funny' \
        'git-diff-tree -z --name-status $t0 $t1 | tr \\0 \\012 >current &&
        diff -u expected current'
 
-echo 'CNUM     no-funny        "tabs\t,\" (dq) and spaces"' >expected
+cat > expected <<\EOF
+CNUM   no-funny        "tabs\t,\" (dq) and spaces"
+EOF
 test_expect_success 'git-diff-tree -C with-funny' \
        'git-diff-tree -C --find-copies-harder --name-status \
                $t0 $t1 | sed -e 's/^C[0-9]*/CNUM/' >current &&
        diff -u expected current'
 
-echo 'RNUM     no-funny        "tabs\t,\" (dq) and spaces"' >expected
+cat > expected <<\EOF
+RNUM   no-funny        "tabs\t,\" (dq) and spaces"
+EOF
 test_expect_success 'git-diff-tree delete with-funny' \
        'git-update-index --force-remove "$p0" &&
        git-diff-index -M --name-status \
                $t0 | sed -e 's/^R[0-9]*/RNUM/' >current &&
        diff -u expected current'
 
-echo 'diff --git a/no-funny "b/tabs\t,\" (dq) and spaces"
+cat > expected <<\EOF
+diff --git a/no-funny "b/tabs\t,\" (dq) and spaces"
 similarity index NUM%
 rename from no-funny
-rename to "tabs\t,\" (dq) and spaces"' >expected
-
+rename to "tabs\t,\" (dq) and spaces"
+EOF
 test_expect_success 'git-diff-tree delete with-funny' \
        'git-diff-index -M -p $t0 |
         sed -e "s/index [0-9]*%/index NUM%/" >current &&
         diff -u expected current'
 
 chmod +x "$p1"
-echo 'diff --git a/no-funny "b/tabs\t,\" (dq) and spaces"
+cat > expected <<\EOF
+diff --git a/no-funny "b/tabs\t,\" (dq) and spaces"
 old mode 100644
 new mode 100755
 similarity index NUM%
 rename from no-funny
-rename to "tabs\t,\" (dq) and spaces"' >expected
-
+rename to "tabs\t,\" (dq) and spaces"
+EOF
 test_expect_success 'git-diff-tree delete with-funny' \
        'git-diff-index -M -p $t0 |
         sed -e "s/index [0-9]*%/index NUM%/" >current &&
         diff -u expected current'
 
-echo >expected ' "tabs\t,\" (dq) and spaces"
- 1 files changed, 0 insertions(+), 0 deletions(-)'
+cat >expected <<\EOF
+ "tabs\t,\" (dq) and spaces"
+ 1 files changed, 0 insertions(+), 0 deletions(-)
+EOF
 test_expect_success 'git-diff-tree rename with-funny applied' \
        'git-diff-index -M -p $t0 |
         git-apply --stat | sed -e "s/|.*//" -e "s/ *\$//" >current &&
         diff -u expected current'
 
-echo >expected ' no-funny
+cat > expected <<\EOF
+ no-funny
  "tabs\t,\" (dq) and spaces"
- 2 files changed, 3 insertions(+), 3 deletions(-)'
-
+ 2 files changed, 3 insertions(+), 3 deletions(-)
+EOF
 test_expect_success 'git-diff-tree delete with-funny applied' \
        'git-diff-index -p $t0 |
         git-apply --stat | sed -e "s/|.*//" -e "s/ *\$//" >current &&
index bdd95c0..323606c 100755 (executable)
@@ -16,25 +16,20 @@ test_expect_success 'prepare repository' \
         echo git >c &&
         cat b b >d'
 
-test_expect_success 'diff without --binary' \
-       'git-diff | git-apply --stat --summary >current &&
-        cmp current - <<\EOF
+cat > expected <<\EOF
  a |    2 +-
  b |  Bin
  c |    2 +-
  d |  Bin
  4 files changed, 2 insertions(+), 2 deletions(-)
-EOF'
+EOF
+test_expect_success 'diff without --binary' \
+       'git-diff | git-apply --stat --summary >current &&
+        cmp current expected'
 
 test_expect_success 'diff with --binary' \
        'git-diff --binary | git-apply --stat --summary >current &&
-        cmp current - <<\EOF
- a |    2 +-
- b |  Bin
- c |    2 +-
- d |  Bin
- 4 files changed, 2 insertions(+), 2 deletions(-)
-EOF'
+        cmp current expected'
 
 # apply needs to be able to skip the binary material correctly
 # in order to report the line number of a corrupt patch.
index 92f12d9..f7625a6 100755 (executable)
@@ -12,11 +12,11 @@ test_description='Testing multi_ack pack fetching
 
 # Some convenience functions
 
-function add () {
-       local name=$1
-       local text="$@"
-       local branch=${name:0:1}
-       local parents=""
+add () {
+       name=$1
+       text="$@"
+       branch=`echo $name | sed -e 's/^\(.\).*$/\1/'`
+       parents=""
 
        shift
        while test $1; do
@@ -36,13 +36,13 @@ function add () {
        eval ${branch}TIP=$commit
 }
 
-function count_objects () {
+count_objects () {
        ls .git/objects/??/* 2>>log2.txt | wc -l | tr -d " "
 }
 
-function test_expect_object_count () {
-       local message=$1
-       local count=$2
+test_expect_object_count () {
+       message=$1
+       count=$2
 
        output="$(count_objects)"
        test_expect_success \
@@ -50,18 +50,18 @@ function test_expect_object_count () {
                "test $count = $output"
 }
 
-function pull_to_client () {
-       local number=$1
-       local heads=$2
-       local count=$3
-       local no_strict_count_check=$4
+pull_to_client () {
+       number=$1
+       heads=$2
+       count=$3
+       no_strict_count_check=$4
 
        cd client
        test_expect_success "$number pull" \
                "git-fetch-pack -k -v .. $heads"
        case "$heads" in *A*) echo $ATIP > .git/refs/heads/A;; esac
        case "$heads" in *B*) echo $BTIP > .git/refs/heads/B;; esac
-       git-symbolic-ref HEAD refs/heads/${heads:0:1}
+       git-symbolic-ref HEAD refs/heads/`echo $heads | sed -e 's/^\(.\).*$/\1/'`
 
        test_expect_success "fsck" 'git-fsck-objects --full > fsck.txt 2>&1'
 
index c6752af..d402621 100755 (executable)
@@ -69,7 +69,9 @@ on_committer_date()
 {
     _date=$1
     shift 1
-    GIT_COMMITTER_DATE=$_date "$@"
+    export GIT_COMMITTER_DATE="$_date"
+    "$@"
+    unset GIT_COMMITTER_DATE
 }
 
 # Execute a command and suppress any error output.
diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh
new file mode 100755 (executable)
index 0000000..a61da1e
--- /dev/null
@@ -0,0 +1,41 @@
+#!/bin/sh
+
+test_description='git-send-email'
+. ./test-lib.sh
+
+PROG='git send-email'
+test_expect_success \
+    'prepare reference tree' \
+    'echo "1A quick brown fox jumps over the" >file &&
+     echo "lazy dog" >>file &&
+     git add file
+     GIT_AUTHOR_NAME="A" git commit -a -m "Initial."'
+
+test_expect_success \
+    'Setup helper tool' \
+    '(echo "#!/bin/sh"
+      echo shift
+      echo for a
+      echo do
+      echo "  echo \"!\$a!\""
+      echo "done >commandline"
+      echo "cat > msgtxt"
+      ) >fake.sendmail
+     chmod +x ./fake.sendmail
+     git add fake.sendmail
+     GIT_AUTHOR_NAME="A" git commit -a -m "Second."'
+
+test_expect_success \
+    'Extract patches and send' \
+    'git format-patch -n HEAD^1
+     git send-email -from="Example <nobody@example.com>" --to=nobody@example.com --smtp-server="$(pwd)/fake.sendmail" ./0001*txt'
+
+cat >expected <<\EOF
+!nobody@example.com!
+!author@example.com!
+EOF
+test_expect_success \
+    'Verify commandline' \
+    'diff commandline expected'
+
+test_done
index fd48742..a1e6bb9 100644 (file)
@@ -1,85 +1,56 @@
 #include "cache.h"
 #include "refs.h"
 
-static const char git_update_ref_usage[] = "git-update-ref <refname> <value> [<oldval>]";
-
-static int re_verify(const char *path, unsigned char *oldsha1, unsigned char *currsha1)
-{
-       char buf[40];
-       int fd = open(path, O_RDONLY), nr;
-       if (fd < 0)
-               return -1;
-       nr = read(fd, buf, 40);
-       close(fd);
-       if (nr != 40 || get_sha1_hex(buf, currsha1) < 0)
-               return -1;
-       return memcmp(oldsha1, currsha1, 20) ? -1 : 0;
-}
+static const char git_update_ref_usage[] =
+"git-update-ref <refname> <value> [<oldval>] [-m <reason>]";
 
 int main(int argc, char **argv)
 {
-       char *hex;
-       const char *refname, *value, *oldval, *path;
-       char *lockpath;
-       unsigned char sha1[20], oldsha1[20], currsha1[20];
-       int fd, written;
+       const char *refname=NULL, *value=NULL, *oldval=NULL, *msg=NULL;
+       struct ref_lock *lock;
+       unsigned char sha1[20], oldsha1[20];
+       int i;
 
        setup_git_directory();
        git_config(git_default_config);
-       if (argc < 3 || argc > 4)
+
+       for (i = 1; i < argc; i++) {
+               if (!strcmp("-m", argv[i])) {
+                       if (i+1 >= argc)
+                               usage(git_update_ref_usage);
+                       msg = argv[++i];
+                       if (!*msg)
+                               die("Refusing to perform update with empty message.");
+                       if (strchr(msg, '\n'))
+                               die("Refusing to perform update with \\n in message.");
+                       continue;
+               }
+               if (!refname) {
+                       refname = argv[i];
+                       continue;
+               }
+               if (!value) {
+                       value = argv[i];
+                       continue;
+               }
+               if (!oldval) {
+                       oldval = argv[i];
+                       continue;
+               }
+       }
+       if (!refname || !value)
                usage(git_update_ref_usage);
 
-       refname = argv[1];
-       value = argv[2];
-       oldval = argv[3];
        if (get_sha1(value, sha1))
                die("%s: not a valid SHA1", value);
        memset(oldsha1, 0, 20);
        if (oldval && get_sha1(oldval, oldsha1))
                die("%s: not a valid old SHA1", oldval);
 
-       path = resolve_ref(git_path("%s", refname), currsha1, !!oldval);
-       if (!path)
-               die("No such ref: %s", refname);
-
-       if (oldval) {
-               if (memcmp(currsha1, oldsha1, 20))
-                       die("Ref %s is at %s but expected %s", refname, sha1_to_hex(currsha1), sha1_to_hex(oldsha1));
-               /* Nothing to do? */
-               if (!memcmp(oldsha1, sha1, 20))
-                       exit(0);
-       }
-       path = strdup(path);
-       lockpath = mkpath("%s.lock", path);
-       if (safe_create_leading_directories(lockpath) < 0)
-               die("Unable to create all of %s", lockpath);
-
-       fd = open(lockpath, O_CREAT | O_EXCL | O_WRONLY, 0666);
-       if (fd < 0)
-               die("Unable to create %s", lockpath);
-       hex = sha1_to_hex(sha1);
-       hex[40] = '\n';
-       written = write(fd, hex, 41);
-       close(fd);
-       if (written != 41) {
-               unlink(lockpath);
-               die("Unable to write to %s", lockpath);
-       }
-
-       /*
-        * Re-read the ref after getting the lock to verify
-        */
-       if (oldval && re_verify(path, oldsha1, currsha1) < 0) {
-               unlink(lockpath);
-               die("Ref lock failed");
-       }
-
-       /*
-        * Finally, replace the old ref with the new one
-        */
-       if (rename(lockpath, path) < 0) {
-               unlink(lockpath);
-               die("Unable to create %s", path);
-       }
+       lock = lock_any_ref_for_update(refname, oldval ? oldsha1 : NULL, 0);
+       if (!lock)
+               return 1;
+       if (write_ref_sha1(lock, sha1, msg) < 0)
+               return 1;
        return 0;
 }