diff options
author | Junio C Hamano <junkio@cox.net> | 2005-08-15 15:53:37 -0700 |
---|---|---|
committer | Junio C Hamano <junkio@cox.net> | 2005-08-15 15:53:37 -0700 |
commit | da27f4f3f4c23d0276a372726ed050ed13e45168 (patch) | |
tree | 8842fbebf0db214d0bc8c68e2918a712b7edede3 | |
parent | Merge master into rc (diff) | |
parent | [PATCH] Add documentation for git repack and git-prune-packed. (diff) | |
download | tgif-da27f4f3f4c23d0276a372726ed050ed13e45168.tar.xz |
Merge master changes into rc.
33 files changed, 1054 insertions, 132 deletions
diff --git a/Documentation/Makefile b/Documentation/Makefile index c887ded6af..336578497d 100644 --- a/Documentation/Makefile +++ b/Documentation/Makefile @@ -53,3 +53,4 @@ clean: %.xml : %.txt asciidoc -b docbook -d manpage $< + diff --git a/Documentation/git-apply-patch-script.txt b/Documentation/git-apply-patch-script.txt index a6f860d424..808d3cdc1b 100644 --- a/Documentation/git-apply-patch-script.txt +++ b/Documentation/git-apply-patch-script.txt @@ -1,6 +1,6 @@ git-apply-patch-script(1) ========================= -v0.1, May 2005 +v0.99.4, May 2005 NAME ---- @@ -20,7 +20,7 @@ family of commands report to the current work tree. Author ------ -Written by Linus Torvalds <torvalds@osdl.org> +Written by Junio C Hamano <junkio@cox.net> Documentation -------------- diff --git a/Documentation/git-commit-script.txt b/Documentation/git-commit-script.txt index bb559d70a5..cf6b5c3da2 100644 --- a/Documentation/git-commit-script.txt +++ b/Documentation/git-commit-script.txt @@ -8,7 +8,7 @@ git-commit-script - Record your changes SYNOPSIS -------- -'git commit' [-a] [(-c | -C) <commit> | -F <file> | -m <msg>] <file>... +'git commit' [-a] [-s] [-v] [(-c | -C) <commit> | -F <file> | -m <msg>] <file>... DESCRIPTION ----------- @@ -36,13 +36,26 @@ OPTIONS -m <msg>:: Use the given <msg> as the commit message. +-s:: + Add Signed-off-by line at the end of the commit message. + +-v:: + Look for suspicious lines the commit introduces, and + abort committing if there is one. 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. + <file>...:: Update specified paths in the index file. Author ------ -Written by Linus Torvalds <torvalds@osdl.org> +Written by Linus Torvalds <torvalds@osdl.org> and +Junio C Hamano <junkio@cox.net> + GIT --- diff --git a/Documentation/git-fetch-script.txt b/Documentation/git-fetch-script.txt index 937df05dbc..db3086c732 100644 --- a/Documentation/git-fetch-script.txt +++ b/Documentation/git-fetch-script.txt @@ -1,6 +1,6 @@ git-fetch-script(1) =================== -v0.1, July 2005 +v0.99.4, Aug 2005 NAME ---- @@ -36,7 +36,8 @@ include::pull-fetch-param.txt[] Author ------ -Written by Linus Torvalds <torvalds@osdl.org> and Junio C Hamano <junkio@cox.net> +Written by Linus Torvalds <torvalds@osdl.org> and +Junio C Hamano <junkio@cox.net> Documentation -------------- diff --git a/Documentation/git-log-script.txt b/Documentation/git-log-script.txt new file mode 100644 index 0000000000..ed359bec86 --- /dev/null +++ b/Documentation/git-log-script.txt @@ -0,0 +1,45 @@ +git-log-script(1) +================= +v0.99.4, Aug 2005 + +NAME +---- +git-log-script - Show commit logs + + +SYNOPSIS +-------- +'git log' <option>... + +DESCRIPTION +----------- +Shows the commit logs. This command internally invokes +'git-rev-list', and the command line options are passed to that +command. + +This manual page describes only the most frequently used options. + +OPTIONS +------- +--pretty=<format>: + Controls the way the commit log is formatted. + +--max-count=<n>:: + Limits the number of commits to show. + +<since>..<until>:: + Show only commits between the named two commits. + + +Author +------ +Written by Linus Torvalds <torvalds@osdl.org> + +Documentation +-------------- +Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>. + +GIT +--- +Part of the link:git.html[git] suite + diff --git a/Documentation/git-merge-one-file-script.txt b/Documentation/git-merge-one-file-script.txt index 387601d7e4..1cfe9548e1 100644 --- a/Documentation/git-merge-one-file-script.txt +++ b/Documentation/git-merge-one-file-script.txt @@ -1,6 +1,6 @@ git-merge-one-file-script(1) ============================ -v0.1, May 2005 +v0.99.4, Aug 2005 NAME ---- @@ -18,7 +18,8 @@ to resolve a merge after the trivial merge done with "git-read-tree -m". Author ------ -Written by Linus Torvalds <torvalds@osdl.org> +Written by Linus Torvalds <torvalds@osdl.org>, +Junio C Hamano <junkio@cox.net> and Petr Baudis <pasky@suse.cz>. Documentation -------------- diff --git a/Documentation/git-pack-objects.txt b/Documentation/git-pack-objects.txt index 9628183143..7710be0851 100644 --- a/Documentation/git-pack-objects.txt +++ b/Documentation/git-pack-objects.txt @@ -74,6 +74,10 @@ Documentation ------------- Documentation by Junio C Hamano +See-Also +-------- +git-repack-script(1) git-prune-packed(1) + GIT --- Part of the link:git.html[git] suite diff --git a/Documentation/git-prune-packed.txt b/Documentation/git-prune-packed.txt new file mode 100644 index 0000000000..893c1df55e --- /dev/null +++ b/Documentation/git-prune-packed.txt @@ -0,0 +1,42 @@ +git-prune-packed(1) +===================== +v0.1, August 2005 + +NAME +---- +git-prune-packed - Program used to remove the extra object files that are now +residing in a pack file. + + +SYNOPSIS +-------- +'git-prune-packed' + +DESCRIPTION +----------- +This program search the GIT_OBJECT_DIR for all objects that currently exist in +a pack file as well as the independent object directories. + +All such extra objects are removed. + +A pack is a collection of objects, individually compressed, with delta +compression applied, stored in a single file, with an associated index file. + +Packs are used to reduce the load on mirror systems, backup engines, disk storage, etc. + +Author +------ +Written by Linus Torvalds <torvalds@osdl.org> + +Documentation +-------------- +Documentation by Ryan Anderson <ryan@michonline.com> + +See-Also +-------- +git-pack-objects(1) git-repack-script(1) + +GIT +--- +Part of the link:git.html[git] suite + diff --git a/Documentation/git-pull-script.txt b/Documentation/git-pull-script.txt index ec1e7a2a90..ad9d3ab315 100644 --- a/Documentation/git-pull-script.txt +++ b/Documentation/git-pull-script.txt @@ -1,6 +1,6 @@ git-pull-script(1) ================== -v0.1, May 2005 +v0.99.4, Aug 2005 NAME ---- @@ -25,7 +25,8 @@ include::pull-fetch-param.txt[] Author ------ -Written by Linus Torvalds <torvalds@osdl.org> and Junio C Hamano <junkio@cox.net> +Written by Linus Torvalds <torvalds@osdl.org> +and Junio C Hamano <junkio@cox.net> Documentation -------------- diff --git a/Documentation/git-repack-script.txt b/Documentation/git-repack-script.txt new file mode 100644 index 0000000000..497d0dfe80 --- /dev/null +++ b/Documentation/git-repack-script.txt @@ -0,0 +1,40 @@ +git-repack-script(1) +===================== +v0.1, August 2005 + +NAME +---- +git-repack-script - Script used to pack a repository from a collection of +objects into pack files. + + +SYNOPSIS +-------- +'git-repack-script' + +DESCRIPTION +----------- +This script is used to combine all objects that do not currently reside in a +"pack", into a pack. + +A pack is a collection of objects, individually compressed, with delta +compression applied, stored in a single file, with an associated index file. + +Packs are used to reduce the load on mirror systems, backup engines, disk storage, etc. + +Author +------ +Written by Linus Torvalds <torvalds@osdl.org> + +Documentation +-------------- +Documentation by Ryan Anderson <ryan@michonline.com> + +See-Also +-------- +git-pack-objects(1) git-prune-packed(1) + +GIT +--- +Part of the link:git.html[git] suite + diff --git a/Documentation/git-resolve-script.txt b/Documentation/git-resolve-script.txt index 8dd84a381a..99c399a073 100644 --- a/Documentation/git-resolve-script.txt +++ b/Documentation/git-resolve-script.txt @@ -1,6 +1,6 @@ git-resolve-script(1) ===================== -v0.1, May 2005 +v0.99.4, Aug 2005 NAME ---- @@ -18,7 +18,8 @@ This script is used by Linus to merge two trees. Author ------ -Written by Linus Torvalds <torvalds@osdl.org> +Written by Linus Torvalds <torvalds@osdl.org> and +Dan Holmsand <holmsand@gmail.com>. Documentation -------------- diff --git a/Documentation/git-shortlog.txt b/Documentation/git-shortlog.txt new file mode 100644 index 0000000000..6968ca6060 --- /dev/null +++ b/Documentation/git-shortlog.txt @@ -0,0 +1,31 @@ +git-shortlog(1) +=============== +v0.99.4, Aug 2005 + +NAME +---- +git-shortlog - Summarize 'git log' output. + + +SYNOPSIS +-------- +'git log --pretty=short | git shortlog' + +DESCRIPTION +----------- +Summarizes 'git log' output in a format suitable for inclusion +in release announcements. + + +Author +------ +Written by Jeff Garzik <jgarzik@pobox.com> + +Documentation +-------------- +Documentation by Junio C Hamano. + +GIT +--- +Part of the link:git.html[git] suite + diff --git a/Documentation/git-show-branches-script.txt b/Documentation/git-show-branches-script.txt new file mode 100644 index 0000000000..95c7013445 --- /dev/null +++ b/Documentation/git-show-branches-script.txt @@ -0,0 +1,69 @@ +git-show-branches-script(1) +=========================== +v0.99.4, Aug 2005 + +NAME +---- +git-show-branches-script - Show branches and their commits. + +SYNOPSIS +-------- +'git show-branches <reference>...' + +DESCRIPTION +----------- +Shows the head commits from the named <reference> (or all refs under +$GIT_DIR/refs/heads), and displays concise list of commit logs +to show their relationship semi-visually. + +OPTIONS +------- +<reference>:: + Name of the reference under $GIT_DIR/refs/heads/. + + +OUTPUT +------ +Given N <references>, the first N lines are the one-line +description from their commit message. The branch head that is +pointed at by $GIT_DIR/HEAD is prefixed with an asterisk '*' +character while other heads are prefixed with a '!' character. + +Following these N lines, one-line log for each commit is +displayed, indented N places. If a commit is on the I-th +branch, the I-th indentation character shows a '+' sign; +otherwise it shows a space. + +The following example shows three branches, "pu", "master" and +"rc": + + * [pu] Add cheap local clone '-s' flag to git-clone-script + ! [master] Documentation updates. + ! [rc] Merge master into rc + + Add cheap local clone '-s' flag to git-clone-script + + Alternate object pool mechanism updates. + + Audit rev-parse users. + ++ Documentation updates. + + Merge master into rc + +++ [PATCH] plug memory leak in diff.c::diff_free_filepair() + +These three branches all forked from a common commit, "[PATCH] +plug memory leak...", and "rc" has one commit ahead of it. The +"master" branch has one different commit that is also shared by +"pu" branch, and "pu" branch has three more commits on top of +"master" branch. + + +Author +------ +Written by Junio C Hamano <junkio@cox.net> + + +Documentation +-------------- +Documentation by Junio C Hamano. + + +GIT +--- +Part of the link:git.html[git] suite diff --git a/Documentation/git-status-script.txt b/Documentation/git-status-script.txt new file mode 100644 index 0000000000..2fb7f74a0f --- /dev/null +++ b/Documentation/git-status-script.txt @@ -0,0 +1,46 @@ +git-status-script(1) +==================== +v0.99.4, Aug 2005 + +NAME +---- +git-status-script - Show working tree status. + + +SYNOPSIS +-------- +'git status' + +DESCRIPTION +----------- +Examines paths in the working tree that has changes unrecorded +to the index file, and changes between the index file and the +current HEAD commit. The former paths are what you _could_ +commit by running 'git-update-cache' before running 'git +commit', and the latter paths are what you _would_ commit by +running 'git commit'. + +If there is no path that is different between the index file and +the current HEAD commit, the command exits with non-zero +status. + + +OUTPUT +------ +The output from this command is designed to be used as a commit +template comments, and all the output lines are prefixed with '#'. + + +Author +------ +Written by Linus Torvalds <torvalds@osdl.org> and +Junio C Hamano <junkio@cox.net>. + +Documentation +-------------- +Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>. + +GIT +--- +Part of the link:git.html[git] suite + diff --git a/Documentation/git-tag-script.txt b/Documentation/git-tag-script.txt index 2df396ef89..5032acb172 100644 --- a/Documentation/git-tag-script.txt +++ b/Documentation/git-tag-script.txt @@ -1,6 +1,6 @@ git-tag-script(1) ================= -v0.1, May 2005 +v0.99.4, Aug 2005 NAME ---- @@ -27,7 +27,8 @@ A GnuPG signed tag object will be created when "-s" is used. Author ------ -Written by Linus Torvalds <torvalds@osdl.org> +Written by Linus Torvalds <torvalds@osdl.org>, +Junio C Hamano <junkio@cox.net> and Chris Wright <chrisw@osdl.org>. Documentation -------------- diff --git a/Documentation/git-whatchanged.txt b/Documentation/git-whatchanged.txt new file mode 100644 index 0000000000..742005353a --- /dev/null +++ b/Documentation/git-whatchanged.txt @@ -0,0 +1,61 @@ +git-whatchanged(1) +================== +v0.99.4, Aug 2005 + +NAME +---- +git-whatchanged - Show logs with difference each commit introduces. + + +SYNOPSIS +-------- +'git whatchanged' <option>... + +DESCRIPTION +----------- +Shows commit logs and diff output each commit introduces. The +command internally invokes 'git-rev-list' piped to +'git-diff-tree', and takes command line options for both of +these commands. + +This manual page describes only the most frequently used options. + + +OPTIONS +------- +-p:: + Show textual diffs, instead of the git internal diff + output format that is useful only to tell the changed + paths and their nature of changes. + +--max-count=<n>:: + Limit output to <n> commits. + +<since>..<until>:: + Limit output to between the two named commits (bottom + exclusive, top inclusive). + +-r:: + Show git internal diff output, but for the whole tree, + not just the top level. + +--pretty=<format>:: + Controls the output format for the commit logs. + <format> can be one of 'raw', 'medium', 'short', 'full', + and 'oneline'. + + +Author +------ +Written by Linus Torvalds <torvalds@osdl.org> and +Junio C Hamano <junkio@cox.net> + + +Documentation +-------------- +Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>. + +GIT +--- +Part of the link:git.html[git] suite + diff --git a/Documentation/git.txt b/Documentation/git.txt index 1308eb675d..27f362626f 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -1,6 +1,6 @@ git(7) ====== -May 2005 +v0.99.4, Aug 2005 NAME ---- @@ -38,7 +38,7 @@ In addition, git itself comes with a spartan set of porcelain commands. They are usable but are not meant to compete with real Porcelains. -There are also some ancilliary programs that can be viewed as useful +There are also some ancillary programs that can be viewed as useful aids for using the core commands but which are unlikely to be used by SCMs layered over git. @@ -71,6 +71,13 @@ link:git-hash-object.html[git-hash-object]:: link:git-write-tree.html[git-write-tree]:: Creates a tree from the current cache +link:git-pack-objects.html[git-pack-objects]:: + Creates a packed archive of objects. + +link:git-prune-packed.html[git-prune-packed]:: + Remove extra objects that are already in pack files. + + Interrogation commands ~~~~~~~~~~~~~~~~~~~~~~ link:git-cat-file.html[git-cat-file]:: @@ -165,8 +172,20 @@ link:git-update-server-info.html[git-update-server-info]:: clients discover references and packs on it. -Porcelainish Commands ---------------------- +Porcelain-ish Commands +---------------------- +link:git-whatchanged.html[git-whatchanged]:: + Shows commit logs and differences they introduce. + +link:git-log-script.html[git-log-script]:: + Shows commit logs. + +link:git-shortlog.html[git-shortlog]:: + Summarizes 'git log' output. + +link:git-status-script.html[git-status-script]:: + Shows the working tree status. + link:git-fetch-script.html[git-fetch-script]:: Download from a remote repository via various protocols. @@ -176,9 +195,15 @@ link:git-pull-script.html[git-pull-script]:: link:git-commit-script.html[git-commit-script]:: Record changes to the repository. +link:git-show-branches-script.html[git-show-branches-script]:: + Show branches and their commits. + +link:git-repack-script.html[git-repack-script]:: + Pack unpacked objects in a repository. + -Ancilliary Commands -------------------- +Ancillary Commands +------------------ Manipulators: link:git-apply-patch-script.html[git-apply-patch-script]:: @@ -200,7 +225,7 @@ link:git-tag-script.html[git-tag-script]:: An example script to create a tag object signed with GPG -Interogators: +Interrogators: link:git-diff-helper.html[git-diff-helper]:: Generates patch format output for git-diff-* @@ -240,7 +265,7 @@ Identifier Terminology Symbolic Identifiers -------------------- -Any git comand accepting any <object> can also use the following +Any git command accepting any <object> can also use the following symbolic notation: HEAD:: @@ -311,7 +336,7 @@ git so take care if using Cogito etc 'GIT_ALTERNATE_OBJECT_DIRECTORIES':: Due to the immutable nature of git objects, old objects can be archived into shared, read-only directories. This variable - specifies a ":" seperated list of git object directories which + specifies a ":" separated list of git object directories which can be used to search for git objects. New objects will not be written to these directories. diff --git a/Documentation/howto/make-dist.txt b/Documentation/howto/make-dist.txt new file mode 100644 index 0000000000..ae9094157c --- /dev/null +++ b/Documentation/howto/make-dist.txt @@ -0,0 +1,47 @@ +Date: Fri, 12 Aug 2005 22:39:48 -0700 (PDT) +From: Linus Torvalds <torvalds@osdl.org> +To: Dave Jones <davej@redhat.com> +cc: git@vger.kernel.org +Subject: Re: Fwd: Re: git checkout -f branch doesn't remove extra files + +On Sat, 13 Aug 2005, Dave Jones wrote: +> +> > Git actually has a _lot_ of nifty tools. I didn't realize that people +> > didn't know about such basic stuff as "git-tar-tree" and "git-ls-files". +> +> Maybe its because things are moving so fast :) Or maybe I just wasn't +> paying attention on that day. (I even read the git changes via RSS, +> so I should have no excuse). + +Well, git-tar-tree has been there since late April - it's actually one of +those really early commands. I'm pretty sure the RSS feed came later ;) + +I use it all the time in doing releases, it's a lot faster than creating a +tar tree by reading the filesystem (even if you don't have to check things +out). A hidden pearl. + +This is my crappy "release-script": + + [torvalds@g5 ~]$ cat bin/release-script + #!/bin/sh + stable="$1" + last="$2" + new="$3" + echo "# git-tag-script v$new" + echo "git-tar-tree v$new linux-$new | gzip -9 > ../linux-$new.tar.gz" + echo "git-diff-tree -p v$stable v$new | gzip -9 > ../patch-$new.gz" + echo "git-rev-list --pretty v$new ^v$last > ../ChangeLog-$new" + echo "git-rev-list --pretty=short v$new ^v$last | git-shortlog > ../ShortLog" + echo "git-diff-tree -p v$last v$new | git-apply --stat > ../diffstat-$new" + +and when I want to do a new kernel release I literally first tag it, and +then do + + release-script 2.6.12 2.6.13-rc6 2.6.13-rc7 + +and check that things look sane, and then just cut-and-paste the commands. + +Yeah, it's stupid. + + Linus + diff --git a/Documentation/howto/rebase-and-edit.txt b/Documentation/howto/rebase-and-edit.txt new file mode 100644 index 0000000000..8299ca5cdc --- /dev/null +++ b/Documentation/howto/rebase-and-edit.txt @@ -0,0 +1,78 @@ +Date: Sat, 13 Aug 2005 22:16:02 -0700 (PDT) +From: Linus Torvalds <torvalds@osdl.org> +To: Steve French <smfrench@austin.rr.com> +cc: git@vger.kernel.org +Subject: Re: sending changesets from the middle of a git tree + +On Sat, 13 Aug 2005, Linus Torvalds wrote: + +> That's correct. Same things apply: you can move a patch over, and create a +> new one with a modified comment, but basically the _old_ commit will be +> immutable. + +Let me clarify. + +You can entirely _drop_ old branches, so commits may be immutable, but +nothing forces you to keep them. Of course, when you drop a commit, you'll +always end up dropping all the commits that depended on it, and if you +actually got somebody else to pull that commit you can't drop it from +_their_ repository, but undoing things is not impossible. + +For example, let's say that you've made a mess of things: you've committed +three commits "old->a->b->c", and you notice that "a" was broken, but you +want to save "b" and "c". What you can do is + + # Create a branch "broken" that is the current code + # for reference + git branch broken + + # Reset the main branch to three parents back: this + # effectively undoes the three top commits + git reset HEAD^^^ + git checkout -f + + # Check the result visually to make sure you know what's + # going on + gitk --all + + # Re-apply the two top ones from "broken" + # + # First "parent of broken" (aka b): + git-diff-tree -p broken^ | git-apply --index + git commit --reedit=broken^ + + # Then "top of broken" (aka c): + git-diff-tree -p broken | git-apply --index + git commit --reedit=broken + +and you've now re-applied (and possibly edited the comments) the two +commits b/c, and commit "a" is basically gone (it still exists in the +"broken" branch, of course). + +Finally, check out the end result again: + + # Look at the new commit history + gitk --all + +to see that everything looks sensible. + +And then, you can just remove the broken branch if you decide you really +don't want it: + + # remove 'broken' branch + rm .git/refs/heads/broken + + # Prune old objects if you're really really sure + git prune + +And yeah, I'm sure there are other ways of doing this. And as usual, the +above is totally untested, and I just wrote it down in this email, so if +I've done something wrong, you'll have to figure it out on your own ;) + + Linus +- +To unsubscribe from this list: send the line "unsubscribe git" in +the body of a message to majordomo@vger.kernel.org +More majordomo info at http://vger.kernel.org/majordomo-info.html + + diff --git a/Documentation/howto/rebase-from-internal-branch.txt b/Documentation/howto/rebase-from-internal-branch.txt new file mode 100644 index 0000000000..8109b7ff26 --- /dev/null +++ b/Documentation/howto/rebase-from-internal-branch.txt @@ -0,0 +1,163 @@ +From: Junio C Hamano <junkio@cox.net> +To: git@vger.kernel.org +Cc: Petr Baudis <pasky@suse.cz>, Linus Torvalds <torvalds@osdl.org> +Subject: Re: sending changesets from the middle of a git tree +Date: Sun, 14 Aug 2005 18:37:39 -0700 + +Petr Baudis <pasky@suse.cz> writes: + +> Dear diary, on Sun, Aug 14, 2005 at 09:57:13AM CEST, I got a letter +> where Junio C Hamano <junkio@cox.net> told me that... +>> Linus Torvalds <torvalds@osdl.org> writes: +>> +>> > Junio, maybe you want to talk about how you move patches from your "pu" +>> > branch to the real branches. +>> +> Actually, wouldn't this be also precisely for what StGIT is intended to? + +Exactly my feeling. I was sort of waiting for Catalin to speak +up. With its basing philosophical ancestry on quilt, this is +the kind of task StGIT is designed to do. + +I just have done a simpler one, this time using only the core +GIT tools. + +I had a handful commits that were ahead of master in pu, and I +wanted to add some documentation bypassing my usual habit of +placing new things in pu first. At the beginning, the commit +ancestry graph looked like this: + + *"pu" head + master --> #1 --> #2 --> #3 + +So I started from master, made a bunch of edits, and committed: + + $ git checkout master + $ cd Documentation; ed git.txt git-apply-patch-script.txt ... + $ cd ..; git add Documentation/*.txt + $ git commit -s -v + +NOTE. The -v flag to commit is a handy way to make sure that +your additions are not introducing bogusly formatted lines. + +After the commit, the ancestry graph would look like this: + + *"pu" head + master^ --> #1 --> #2 --> #3 + \ + \---> master + +The old master is now master^ (the first parent of the master). +The new master commit holds my documentation updates. + +Now I have to deal with "pu" branch. + +This is the kind of situation I used to have all the time when +Linus was the maintainer and I was a contributor, when you look +at "master" branch being the "maintainer" branch, and "pu" +branch being the "contributor" branch. Your work started at the +tip of the "maintainer" branch some time ago, you made a lot of +progress in the meantime, and now the maintainer branch has some +other commits you do not have yet. And "git rebase" was written +with the explicit purpose of helping to maintain branches like +"pu". You _could_ merge master to pu and keep going, but if you +eventually want to cherrypick and merge some but not necessarily +all changes back to the master branch, it often makes later +operations for _you_ easier if you rebase (i.e. carry forward +your changes) "pu" rather than merge. So I ran "git rebase": + + $ git checkout pu + $ git rebase master pu + +What this does is to pick all the commits since the current +branch (note that I now am on "pu" branch) forked from the +master branch, and forward port these changes. + + master^ --> #1 --> #2 --> #3 + \ *"pu" head + \---> master --> #1' --> #2' --> #3' + +The diff between master^ and #1 is applied to master and +committed to create #1' commit with the commit information (log, +author and date) taken from commit #1. On top of that #2' and #3' +commits are made similarly out of #2 and #3 commits. + +Old #3 is not recorded in any of the .git/refs/heads/ file +anymore, so after doing this you will have dangling commit if +you ran fsck-cache, which is normal. After testing "pu", you +can run "git prune" to get rid of those original three commits. + +While I am talking about "git rebase", I should talk about how +to do cherrypicking using only the core GIT tools. + +Let's go back to the earlier picture, with different labels. + +You, as an individual developer, cloned upstream repository and +amde a couple of commits on top of it. + + *your "master" head + upstream --> #1 --> #2 --> #3 + +You would want changes #2 and #3 incorporated in the upstream, +while you feel that #1 may need further improvements. So you +prepare #2 and #3 for e-mail submission. + + $ git format-patch master^^ master + +This creates two files, 0001-XXXX.txt and 0002-XXXX.txt. Send +them out "To: " your project maintainer and "Cc: " your mailing +list. You could use contributed script git-send-email-script if +your host has necessary perl modules for this, but your usual +MUA would do as long as it does not corrupt whitespaces in the +patch. + +Then you would wait, and you find out that the upstream picked +up your changes, along with other changes. + + where *your "master" head + upstream --> #1 --> #2 --> #3 + used \ + to be \--> #A --> #2' --> #3' --> #B --> #C + *upstream head + +The two commits #2' and #3' in the above picture record the same +changes your e-mail submission for #2 and #3 contained, but +probably with the new sign-off line added by the upsteam +maintainer and definitely with different committer and ancestry +information, they are different objects from #2 and #3 commits. + +You fetch from upstream, but not merge. + + $ git fetch upstream + +This leaves the updated upstream head in .git/FETCH_HEAD but +does not touch your .git/HEAD nor .git/refs/heads/master. +You run "git rebase" now. + + $ git rebase FETCH_HEAD master + +Earlier, I said that rebase applies all the commits from your +branch on top of the upstream head. Well, I lied. "git rebase" +is a bit smarter than that and notices that #2 and #3 need not +be applied, so it only applies #1. The commit ancestry graph +becomes something like this: + + where *your old "master" head + upstream --> #1 --> #2 --> #3 + used \ your new "master" head* + to be \--> #A --> #2' --> #3' --> #B --> #C --> #1' + *upstream + head + +Again, "git prune" would discard the disused commits #1-#3 and +you continue on starting from the new "master" head, which is +the #1' commit. + +-jc + +- +To unsubscribe from this list: send the line "unsubscribe git" in +the body of a message to majordomo@vger.kernel.org +More majordomo info at http://vger.kernel.org/majordomo-info.html + + diff --git a/Documentation/howto/using-topic-branches.txt b/Documentation/howto/using-topic-branches.txt new file mode 100644 index 0000000000..de28cf7ce7 --- /dev/null +++ b/Documentation/howto/using-topic-branches.txt @@ -0,0 +1,153 @@ +Date: Mon, 15 Aug 2005 12:17:41 -0700 +From: tony.luck@intel.com +Subject: Some tutorial text (was git/cogito workshop/bof at linuxconf au?) + +Here's something that I've been putting together on how I'm using +GIT as a Linux subsystem maintainer. + +I suspect that I'm a bit slap-happy with the "git checkout" commands in +the examples below, and perhaps missing some of the _true-git_ ways of +doing things. + +-Tony + +Linux subsystem maintenance using GIT +------------------------------------- + +My requirements here are to be able to create two public trees: + +1) A "test" tree into which patches are initially placed so that they +can get some exposure when integrated with other ongoing development. +This tree is available to Andrew for pulling into -mm whenever he wants. + +2) A "release" tree into which tested patches are moved for final +sanity checking, and as a vehicle to send them upstream to Linus +(by sending him a "please pull" request.) + +Note that the period of time that each patch spends in the "test" tree +is dependent on the complexity of the change. Since GIT does not support +cherry picking, it is not practical to simply apply all patches to the +test tree and then pull to the release tree as that would leave trivial +patches blocked in the test tree waiting for complex changes to accumulate +enough test time to graduate. + +Back in the BitKeeper days I achieved this my creating small forests of +temporary trees, one tree for each logical grouping of patches, and then +pulling changes from these trees first to the test tree, and then to the +release tree. At first I replicated this in GIT, but then I realised +that I could so this far more efficiently using branches inside a single +GIT repository. + +So here is the step-by-step guide how this all works for me. + +First create your work tree by cloning Linus's public tree: + + $ git clone rsync://rsync.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git work + +Change directory into the cloned tree you just created + + $ cd work + +Make a GIT branch named "linus", and rename the "origin" branch as linus too: + + $ git checkout -b linus + $ mv .git/branches/origin .git/branches/linus + +The "linus" branch will be used to track the upstream kernel. To update it, +you simply run: + + $ git checkout linus && git pull linus + +you can do this frequently (as long as you don't have any uncommited work +in your tree). + +If you need to keep track of other public trees, you can add branches for +them too: + + $ git checkout -b another linus + $ echo URL-for-another-public-tree > .git/branches/another + +Now create the branches in which you are going to work, these start +out at the current tip of the linus branch. + + $ git checkout -b test linus + $ git checkout -b release linus + +These can be easily kept up to date by merging from the "linus" branch: + + $ git checkout test && git resolve test linus "Auto-update from upstream" + $ git checkout release && git resolve release linus "Auto-update from upstream" + +Set up so that you can push upstream to your public tree: + + $ echo master.kernel.org:/ftp/pub/scm/linux/kernel/git/aegl/linux-2.6.git > .git/branches/origin + +and then push each of the test and release branches using: + + $ git push origin test +and + $ git push origin release + +Now to apply some patches from the community. Think of a short +snappy name for a branch to hold this patch (or related group of +patches), and create a new branch from the current tip of the +linus branch: + + $ git checkout -b speed-up-spinlocks linus + +Now you apply the patch(es), run some tests, and commit the change(s). If +the patch is a multi-part series, then you should apply each as a separate +commit to this branch. + + $ ... patch ... test ... commit [ ... patch ... test ... commit ]* + +When you are happy with the state of this change, you can pull it into the +"test" branch in preparation to make it public: + + $ git checkout test && git resolve test speed-up-spinlocks "Pull speed-up-spinlock changes" + +It is unlikely that you would have any conflicts here ... but you might if you +spent a while on this step and had also pulled new versions from upstream. + +Some time later when enough time has passed and testing done, you can pull the +same branch into the "release" tree ready to go upstream. This is where you +see the value of keeping each patch (or patch series) in its own branch. It +means that the patches can be moved into the "release" tree in any order. + + $ git checkout release && git resolve release speed-up-spinlocks "Pull speed-up-spinlock changes" + +After a while, you will have a number of branches, and despite the +well chosen names you picked for each of them, you may forget what +they are for, or what status they are in. To get a reminder of what +changes are in a specific branch, use: + + $ git-whatchanged branchname ^linus | git-shortlog + +To see whether it has already been merged into the test or release branches +use: + + $ git-rev-list branchname ^test +or + $ git-rev-list branchname ^release + +[If this branch has not yet been merged you will see a set of SHA1 values +for the commits, if it has been merged, then there will be no output] + +Once a patch completes the great cycle (moving from test to release, then +pulled by Linus, and finally coming back into your local "linus" branch) +the branch for this change is no longer needed. You detect this when the +output from: + + $ git-rev-list branchname ^linus + +is empty. At this point the branch can be deleted: + + $ rm .git/refs/heads/branchname + +To create diffstat and shortlog summaries of changes to include in a "please +pull" request to Linus you can use: + + $ git-whatchanged -p release ^linus | diffstat -p1 +and + $ git-whatchanged release ^linus | git-shortlog + @@ -71,6 +71,7 @@ SCRIPTS=git git-apply-patch-script git-merge-one-file-script git-prune-script \ SCRIPTS += git-count-objects-script # SCRIPTS += git-send-email-script SCRIPTS += git-revert-script +SCRIPTS += git-show-branches-script PROG= git-update-cache git-diff-files git-init-db git-write-tree \ git-read-tree git-commit-tree git-cat-file git-fsck-cache \ @@ -278,9 +278,10 @@ struct checkout { extern int checkout_entry(struct cache_entry *ce, struct checkout *state); extern struct alternate_object_database { - char *base; + struct alternate_object_database *next; char *name; -} *alt_odb; + char base[0]; /* more */ +} *alt_odb_list; extern void prepare_alt_odb(void); extern struct packed_git { diff --git a/fsck-cache.c b/fsck-cache.c index e40c64332f..8091780193 100644 --- a/fsck-cache.c +++ b/fsck-cache.c @@ -456,13 +456,13 @@ int main(int argc, char **argv) fsck_head_link(); fsck_object_dir(get_object_directory()); if (check_full) { - int j; + struct alternate_object_database *alt; struct packed_git *p; prepare_alt_odb(); - for (j = 0; alt_odb[j].base; j++) { + for (alt = alt_odb_list; alt; alt = alt->next) { char namebuf[PATH_MAX]; - int namelen = alt_odb[j].name - alt_odb[j].base; - memcpy(namebuf, alt_odb[j].base, namelen); + int namelen = alt->name - alt->base; + memcpy(namebuf, alt->base, namelen); namebuf[namelen - 1] = 0; fsck_object_dir(namebuf); } @@ -1,19 +1,22 @@ #!/bin/sh -cmd="$1" -shift -if which git-$cmd-script >& /dev/null -then - exec git-$cmd-script "$@" -fi -if which git-$cmd >& /dev/null -then - exec git-$cmd "$@" -fi +cmd= +path=$(dirname $0) +case "$#" in +0) ;; +*) cmd="$1" + shift + test -x $path/git-$cmd-script && exec $path/git-$cmd-script "$@" + test -x $path/git-$cmd && exec $path/git-$cmd "$@" ;; +esac -alternatives=($(echo $PATH | tr ':' '\n' | while read i; do ls $i/git-*-script 2> /dev/null; done)) +echo "Usage: git COMMAND [OPTIONS] [TARGET]" +if [ -n "$cmd" ]; then + echo " git command '$cmd' not found: commands are:" +else + echo " git commands are:" +fi -echo Git command "'$cmd'" not found. Try one of -for i in "${alternatives[@]}"; do - echo $i | sed 's:^.*/git-: :' | sed 's:-script$::' -done | sort | uniq +alternatives=$(cd $path && + ls git-*-script | sed -e 's/git-//' -e 's/-script//') +echo $alternatives | fmt | sed 's/^/ /' diff --git a/git-clone-script b/git-clone-script index 60dc2a9d88..909ccc5301 100755 --- a/git-clone-script +++ b/git-clone-script @@ -6,7 +6,7 @@ # Clone a repository into a different directory that does not yet exist. usage() { - echo >&2 "* git clone [-l] [-q] [-u <upload-pack>] <repo> <dir>" + echo >&2 "* git clone [-l [-s]] [-q] [-u <upload-pack>] <repo> <dir>" exit 1 } @@ -16,11 +16,14 @@ get_repo_base() { quiet= use_local=no +local_shared=no upload_pack= while case "$#,$1" in 0,*) break ;; *,-l|*,--l|*,--lo|*,--loc|*,--loca|*,--local) use_local=yes ;; + *,-s|*,--s|*,--sh|*,--sha|*,--shar|*,--share|*,--shared) + local_shared=yes ;; *,-q|*,--quiet) quiet=-q ;; 1,-u|1,--upload-pack) usage ;; *,-u|*,--upload-pack) @@ -57,22 +60,30 @@ yes,yes) exit 1 } - # See if we can hardlink and drop "l" if not. - sample_file=$(cd "$repo" && \ - find objects -type f -print | sed -e 1q) + case "$local_shared" in + no) + # See if we can hardlink and drop "l" if not. + sample_file=$(cd "$repo" && \ + find objects -type f -print | sed -e 1q) - # objects directory should not be empty since we are cloning! - test -f "$repo/$sample_file" || exit + # objects directory should not be empty since we are cloning! + test -f "$repo/$sample_file" || exit - l= - if ln "$repo/$sample_file" "$D/.git/objects/sample" 2>/dev/null - then - l=l - fi && - rm -f "$D/.git/objects/sample" && - cd "$repo" && - find objects -type f -print | - cpio -puamd$l "$D/.git/" || exit 1 + l= + if ln "$repo/$sample_file" "$D/.git/objects/sample" 2>/dev/null + then + l=l + fi && + rm -f "$D/.git/objects/sample" && + cd "$repo" && + find objects -type f -print | + cpio -puamd$l "$D/.git/" || exit 1 + ;; + yes) + mkdir -p "$D/.git/objects/info" + echo "$repo/objects" >"$D/.git/objects/info/alternates" + ;; + esac # Make a duplicate of refs and HEAD pointer HEAD= diff --git a/git-rebase-script b/git-rebase-script index 7779813d11..026225ab2c 100755 --- a/git-rebase-script +++ b/git-rebase-script @@ -28,7 +28,7 @@ case "$#" in esac git-read-tree -m -u $ours $upstream && -echo "$upstream" >"$GIT_DIR/HEAD" || exit +git-rev-parse --verify "$upstream^0" >"$GIT_DIR/HEAD" || exit tmp=.rebase-tmp$$ fail=$tmp-fail diff --git a/git-reset-script b/git-reset-script index 49994df7a2..7da8d86823 100755 --- a/git-reset-script +++ b/git-reset-script @@ -1,7 +1,7 @@ #!/bin/sh . git-sh-setup-script || die "Not a git archive" -rev=$(git-rev-parse --revs-only --verify --default HEAD "$@") || exit -rev=$(git-rev-parse --revs-only --verify $rev^0) || exit +rev=$(git-rev-parse --verify --default HEAD "$@") || exit +rev=$(git-rev-parse --verify $rev^0) || exit git-read-tree --reset "$rev" && { if orig=$(git-rev-parse --verify HEAD 2>/dev/null) then diff --git a/git-show-branches-script b/git-show-branches-script new file mode 100755 index 0000000000..263025c476 --- /dev/null +++ b/git-show-branches-script @@ -0,0 +1,53 @@ +#!/bin/sh +# +# Show refs and their recent commits. +# + +. git-sh-setup-script || die "Not a git repository" + +headref=`readlink $GIT_DIR/HEAD` +case "$#" in +0) + set x `cd $GIT_DIR/refs && + find heads -type f -print | + sed -e 's|heads/||' | + sort` + shift ;; +esac + +hh= in= +for ref +do + case "/$headref" in + */"$ref") H='*' ;; + *) H='!' ;; + esac + h=`git-rev-parse --verify "$ref^0"` || exit + l=`git-log-script --max-count=1 --pretty=oneline "$h" | + sed -e 's/^[^ ]* //'` + hh="$hh $h" + echo "$in$H [$ref] $l" + in="$in " +done +set x $hh +shift + +git-rev-list --pretty=oneline "$@" | +while read v l +do + in='' + for h + do + b=`git-merge-base $h $v` + case "$b" in + $v) in="$in+" ;; + *) in="$in " ;; + esac + done + + echo "$in $l" + case "$in" in + *' '*) ;; + *) break ;; + esac +done diff --git a/git-status-script b/git-status-script index e9a0383441..947cc21975 100755 --- a/git-status-script +++ b/git-status-script @@ -1,4 +1,6 @@ #!/bin/sh +. git-sh-setup-script || die "Not a git archive" + report () { header="# # $1: @@ -26,7 +28,7 @@ report () { [ "$header" ] } -git-update-cache --refresh >& /dev/null +git-update-cache --refresh >/dev/null 2>&1 git-diff-cache -M --cached HEAD | sed 's/^://' | report "Updated but not checked in" "will commit" committable="$?" git-diff-files | sed 's/^://' | report "Changed but not updated" "use git-update-cache to mark for commit" @@ -35,4 +37,10 @@ then echo "nothing to commit" exit 1 fi +branch=`readlink "$GIT_DIR/HEAD"` +case "$branch" in +refs/heads/master) ;; +*) echo "# +# On branch $branch" ;; +esac exit 0 diff --git a/git-tag-script b/git-tag-script index d3074a8b3d..39c3c53987 100755 --- a/git-tag-script +++ b/git-tag-script @@ -47,7 +47,7 @@ if [ -e "$GIT_DIR/refs/tags/$name" -a -z "$force" ]; then fi shift -object=$(git-rev-parse --verify --revs-only --default HEAD "$@") || exit 1 +object=$(git-rev-parse --verify --default HEAD "$@") || exit 1 type=$(git-cat-file -t $object) || exit 1 tagger=$(git-var GIT_COMMITTER_IDENT) || exit 1 diff --git a/sha1_file.c b/sha1_file.c index 8d189d4919..2d109f928b 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -222,84 +222,100 @@ char *sha1_pack_index_name(const unsigned char *sha1) return base; } -struct alternate_object_database *alt_odb; +struct alternate_object_database *alt_odb_list; +static struct alternate_object_database **alt_odb_tail; /* * Prepare alternate object database registry. - * alt_odb points at an array of struct alternate_object_database. - * This array is terminated with an element that has both its base - * and name set to NULL. alt_odb[n] comes from n'th non-empty - * element from colon separated ALTERNATE_DB_ENVIRONMENT environment - * variable, and its base points at a statically allocated buffer - * that contains "/the/directory/corresponding/to/.git/objects/...", - * while its name points just after the slash at the end of - * ".git/objects/" in the example above, and has enough space to hold - * 40-byte hex SHA1, an extra slash for the first level indirection, - * and the terminating NUL. - * This function allocates the alt_odb array and all the strings - * pointed by base fields of the array elements with one xmalloc(); - * the string pool immediately follows the array. + * + * The variable alt_odb_list points at the list of struct + * alternate_object_database. The elements on this list come from + * non-empty elements from colon separated ALTERNATE_DB_ENVIRONMENT + * environment variable, and $GIT_OBJECT_DIRECTORY/info/alternates, + * whose contents is exactly in the same format as that environment + * variable. Its base points at a statically allocated buffer that + * contains "/the/directory/corresponding/to/.git/objects/...", while + * its name points just after the slash at the end of ".git/objects/" + * in the example above, and has enough space to hold 40-byte hex + * SHA1, an extra slash for the first level indirection, and the + * terminating NUL. */ -void prepare_alt_odb(void) +static void link_alt_odb_entries(const char *alt, const char *ep) { - int pass, totlen, i; const char *cp, *last; - char *op = NULL; - const char *alt = gitenv(ALTERNATE_DB_ENVIRONMENT) ? : ""; + struct alternate_object_database *ent; + + last = alt; + do { + for (cp = last; cp < ep && *cp != ':'; cp++) + ; + if (last != cp) { + /* 43 = 40-byte + 2 '/' + terminating NUL */ + int pfxlen = cp - last; + int entlen = pfxlen + 43; + + ent = xmalloc(sizeof(*ent) + entlen); + *alt_odb_tail = ent; + alt_odb_tail = &(ent->next); + ent->next = NULL; + + memcpy(ent->base, last, pfxlen); + ent->name = ent->base + pfxlen + 1; + ent->base[pfxlen] = ent->base[pfxlen + 3] = '/'; + ent->base[entlen-1] = 0; + } + while (cp < ep && *cp == ':') + cp++; + last = cp; + } while (cp < ep); +} - if (alt_odb) +void prepare_alt_odb(void) +{ + char path[PATH_MAX]; + char *map, *ep; + int fd; + struct stat st; + char *alt = gitenv(ALTERNATE_DB_ENVIRONMENT) ? : ""; + + sprintf(path, "%s/info/alternates", get_object_directory()); + if (alt_odb_tail) + return; + alt_odb_tail = &alt_odb_list; + link_alt_odb_entries(alt, alt + strlen(alt)); + + fd = open(path, O_RDONLY); + if (fd < 0) + return; + if (fstat(fd, &st) || (st.st_size == 0)) { + close(fd); return; - /* The first pass counts how large an area to allocate to - * hold the entire alt_odb structure, including array of - * structs and path buffers for them. The second pass fills - * the structure and prepares the path buffers for use by - * fill_sha1_path(). - */ - for (totlen = pass = 0; pass < 2; pass++) { - last = alt; - i = 0; - do { - cp = strchr(last, ':') ? : last + strlen(last); - if (last != cp) { - /* 43 = 40-byte + 2 '/' + terminating NUL */ - int pfxlen = cp - last; - int entlen = pfxlen + 43; - if (pass == 0) - totlen += entlen; - else { - alt_odb[i].base = op; - alt_odb[i].name = op + pfxlen + 1; - memcpy(op, last, pfxlen); - op[pfxlen] = op[pfxlen + 3] = '/'; - op[entlen-1] = 0; - op += entlen; - } - i++; - } - while (*cp && *cp == ':') - cp++; - last = cp; - } while (*cp); - if (pass) - break; - alt_odb = xmalloc(sizeof(*alt_odb) * (i + 1) + totlen); - alt_odb[i].base = alt_odb[i].name = NULL; - op = (char*)(&alt_odb[i+1]); } + map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + close(fd); + if (map == MAP_FAILED) + return; + + /* Remove the trailing newline */ + for (ep = map + st.st_size - 1; map < ep && ep[-1] == '\n'; ep--) + ; + link_alt_odb_entries(map, ep); + munmap(map, st.st_size); } static char *find_sha1_file(const unsigned char *sha1, struct stat *st) { - int i; char *name = sha1_file_name(sha1); + struct alternate_object_database *alt; if (!stat(name, st)) return name; prepare_alt_odb(); - for (i = 0; (name = alt_odb[i].name) != NULL; i++) { + for (alt = alt_odb_list; alt; alt = alt->next) { + name = alt->name; fill_sha1_path(name, sha1); - if (!stat(alt_odb[i].base, st)) - return alt_odb[i].base; + if (!stat(alt->base, st)) + return alt->base; } return NULL; } @@ -522,18 +538,18 @@ static void prepare_packed_git_one(char *objdir) void prepare_packed_git(void) { - int i; static int run_once = 0; + struct alternate_object_database *alt; - if (run_once++) + if (run_once) return; - prepare_packed_git_one(get_object_directory()); prepare_alt_odb(); - for (i = 0; alt_odb[i].base != NULL; i++) { - alt_odb[i].name[0] = 0; - prepare_packed_git_one(alt_odb[i].base); + for (alt = alt_odb_list; alt; alt = alt->next) { + alt->name[0] = 0; + prepare_packed_git_one(alt->base); } + run_once = 1; } int check_sha1_signature(const unsigned char *sha1, void *map, unsigned long size, const char *type) diff --git a/templates/hooks--update b/templates/hooks--update index 540ade0d52..0726975367 100644 --- a/templates/hooks--update +++ b/templates/hooks--update @@ -1,6 +1,7 @@ #!/bin/sh # # An example hook script to mail out commit update information. +# Called by git-receive-pack with arguments: refname sha1-old sha1-new # # To enable this hook: # (1) change the recipient e-mail address @@ -12,10 +13,15 @@ recipient="commit-list@mydomain.xz" if expr "$2" : '0*$' >/dev/null then echo "Created a new ref, with the following commits:" - git-rev-list --pretty "$2" + git-rev-list --pretty "$3" else - echo "New commits:" - git-rev-list --pretty "$3" "^$2" + $base=$(git-merge-base "$2" "$3") + if [ $base == "$2" ]; then + echo "New commits:" + else + echo "Rebased ref, commits from common ancestor:" +fi +git-rev-list --pretty "$3" "^$base" fi | mail -s "Changes to ref $1" "$recipient" exit 0 |