├── README.markdown ├── VsSubversion.markdown ├── client └── commit-msg-trac ├── scripts ├── checkout ├── create-gitconfig ├── create-stable ├── pull ├── push └── refollow ├── server ├── functions ├── post-receive-commitnumbers ├── post-receive-email ├── post-receive-gitconfig ├── post-receive-hudson ├── post-receive-trac ├── post-receive-trac.py ├── post-receive.sample ├── update-allow-tags-branches ├── update-ensure-follows ├── update-ensure-ticket-reference ├── update-lock-check ├── update-stable └── update.sample ├── tests ├── .gitignore ├── Makefile ├── t1000-commit-msg-trac.sh ├── t1001-commit-msg-trac-merges.sh ├── t2000-update-ensure-ticket-reference.sh ├── t2001-update-ensure-ticket-reference-merges.sh ├── t2002-update-ensure-ticket-reference-branches.sh ├── t2100-update-stable.sh ├── t2200-1.txt ├── t2200-2.txt ├── t2200-post-receive-email.sh ├── t2201-1.txt ├── t2201-2.txt ├── t2201-3.txt ├── t2201-4.txt ├── t2201-5.txt ├── t2201-6.txt ├── t2201-7.txt ├── t2201-8.txt ├── t2201-post-receive-email-tags.sh ├── t2202-1.txt ├── t2202-2.txt ├── t2202-3.txt ├── t2202-4.txt ├── t2202-5.txt ├── t2202-6.txt ├── t2202-post-receive-email-branches.sh ├── t2203-1.txt ├── t2203-2.txt ├── t2203-post-receive-email-stable.sh ├── t2204-1.txt ├── t2204-post-receive-email-conflicts.sh ├── t2700-ensure-follows.sh ├── t2800-post-receive-gitconfig.sh ├── t2801-post-receive-gitconfig-hooks.sh ├── t2900-update-lock-check.sh ├── t3000-post-receive-trac.sh ├── t3001-post-receive-trac-with-commitnumbers.sh ├── t3100-update-allow-tags-branches.sh ├── t3200-post-receive-commitnumbers.sh ├── t5000-checkout.sh ├── t5100-push.sh ├── t5200-pull.sh ├── t5400-refollow.sh ├── t9000-notes.sh └── test-lib.sh └── workflow.markdown /README.markdown: -------------------------------------------------------------------------------- 1 | 2 | Overview 3 | ======== 4 | 5 | Tweaks for using git in a corporate/close-team environment. 6 | 7 | Scripts 8 | ======= 9 | 10 | * [checkout][12]: does the right thing for creating/tracking a new or existing remote branch 11 | * [push][13]: pushes only the current branch to origin 12 | * [pull][14]: pulls changes down but with `rebase-i-p` to avoid same-branch merges and commit replays 13 | 14 | [12]: /stephenh/git-central/blob/master/scripts/checkout 15 | [13]: /stephenh/git-central/blob/master/scripts/push 16 | [14]: /stephenh/git-central/blob/master/scripts/pull 17 | 18 | Server-side Hooks 19 | ================= 20 | 21 | See the individual scripts for documentation, but an overview: 22 | 23 | * [post-receive-commitnumbers][1]: makes Subversion-like monotonically increasing commit numbers for every commit 24 | * [post-receive-email][2]: contrib email script with customizations for stuff like combined diffs 25 | * [post-receive-gitconfig][3]: auto-updates the git config+hooks on the server when updated in the repo 26 | * [post-receive-hudson][4]: auto-creates new jobs in Hudson when branches in git are created 27 | * [post-receive-trac][5]: updates trac tickets with messages referencing the commits 28 | * [update-allow-tags-branches][6]: contrib/example branch/tag enforcement script with customizations 29 | * [update-ensure-follows][7]: allows nomination of special branches (e.g. stable) that everyone must have merged 30 | * [update-ensure-ticket-reference][8]: enforces ticket references in commit messages (e.g. for trac) 31 | * [update-lock-check][9]: enforces locked/preserved branches 32 | * [update-stable][10]: enforces proper movement of stable 33 | 34 | [1]: /stephenh/git-central/blob/master/server/post-receive-commitnumbers 35 | [2]: /stephenh/git-central/blob/master/server/post-receive-email 36 | [3]: /stephenh/git-central/blob/master/server/post-receive-gitconfig 37 | [4]: /stephenh/git-central/blob/master/server/post-receive-hudson 38 | [5]: /stephenh/git-central/blob/master/server/post-receive-trac 39 | [6]: /stephenh/git-central/blob/master/server/update-allow-tags-branches 40 | [7]: /stephenh/git-central/blob/master/server/update-ensure-follows 41 | [8]: /stephenh/git-central/blob/master/server/update-ensure-ticket-reference 42 | [9]: /stephenh/git-central/blob/master/server/update-lost-check 43 | [10]: /stephenh/git-central/blob/master/server/update-stable 44 | 45 | Client-side Hooks 46 | ================= 47 | 48 | * [commit-msg-trac][11]: enforces ticket references in commit messages 49 | 50 | [11]: /stephenh/git-central/blob/master/client/commit-msg-trac 51 | 52 | Bootstrapping Scripts 53 | ===================== 54 | 55 | * [create-gitconfig][15]: creates a new DAG for managing repository-specific configuration (works with [post-receive-gitconfig][3]) 56 | * [create-stable][16]: creates a new DAG for the first release to merge in to (works with [update-stable][10]) 57 | 58 | [15]: /stephenh/git-central/blob/master/scripts/create-gitconfig 59 | [16]: /stephenh/git-central/blob/master/scripts/create-stable 60 | 61 | Install Server-side Hooks 62 | ========================= 63 | 64 | * Download/clone the `gc` repo to something like `/srv/git/gc` 65 | * Edit `your_repo.git/hooks/post-receive` and `your_repo.git/hooks/update` to call the `gc` hooks as appropriate 66 | * [post-receive.sample][17] and [update.sample][18] are good templates to use for calling multiple hooks 67 | 68 | [17]: /stephenh/git-central/blob/master/server/post-receive.sample 69 | [18]: /stephenh/git-central/blob/master/server/update.sample 70 | 71 | Todo 72 | ==== 73 | 74 | * Install approach for scripts 75 | * Install for client hooks 76 | 77 | -------------------------------------------------------------------------------- /VsSubversion.markdown: -------------------------------------------------------------------------------- 1 | 2 | Svn 3 | === 4 | 5 | * pro: Monotic rev numbers (though commitnumbers is better as they don't change with the whole repo) 6 | * pro: Cherry picking doesn't duplicate the commit 7 | * pro: Good/faster trac integration 8 | * pro: Inherently flattened branches (requires branch.name.rebase+pending preservemerges--or `pull`) 9 | * con: allows anti-social change hoarding of one large commit 10 | * con: lots of .svn meta folders (slows down Eclipse) 11 | * con: looses data in rename+merge scenario 12 | * con: allows pushing from an out-of-date working copy as long as specific files don't conflict 13 | 14 | Git 15 | === 16 | 17 | * pro: index (easier to break up commits, see only conflicts during merging) 18 | * pro: stash (or local WIP on multiple branches without separate working copies) 19 | * pro: local commits 20 | * pro: combined diffs in gitk and commit emails 21 | * pro: "git diff" in conflicted merges only shows conflicts, not what merged cleanly 22 | * pro: safe merging (working copy is not munged, always have ORIG_HEAD or reflog) 23 | * pro: pre-filled-in merge commit messages (e.g. with what conflicted) 24 | * pro: DAG visualization (gitk) 25 | * pro: just one .git meta folder 26 | * con: requires flags/prefer-rebase script to maintain flattened branches 27 | * con: allows anti-social change hoarding of many small commits 28 | * con: no good tattoo (fixed with commitnumbers) 29 | * con: trac integration is slow 30 | 31 | -------------------------------------------------------------------------------- /client/commit-msg-trac: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if test -f $GIT_DIR/MERGE_MSG ; then 4 | exit 0 5 | fi 6 | 7 | grep -i '\(\(re\|refs\|qa\) #[0-9]\+\)\|\(no ticket\)' "$1" > /dev/null 8 | 9 | if [ $? -ne 0 ] 10 | then 11 | echo "Please reference a ticket" 12 | exit 1 13 | fi 14 | 15 | -------------------------------------------------------------------------------- /scripts/checkout: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Makes checkout "just work" in terms of checking out the branch whether or not 4 | # it exists locally or remotely. 5 | # 6 | # If the branch exists locally (e.g. foo), then just check it out 7 | # If the branch exists remotely (e.g. origin/foo), then create a new local branch 8 | # If the branch does not exist remote, then make a new local branch and push it 9 | # 10 | 11 | branch_name=$1 12 | 13 | # See if it already exists 14 | git rev-parse --verify --quiet $branch_name >/dev/null 15 | if [[ $? -eq 0 ]] ; then 16 | # Just checkout what they asked 17 | git checkout "$branch_name" 18 | else 19 | # Make sure we have the latest origin/$branch_name to branch 20 | git fetch 21 | exists_remote=$(git branch -r | grep -x " origin/$branch_name" | wc -l) 22 | if [[ $exists_remote -eq 0 ]] ; then 23 | # Specifying stable to get the last released code 24 | git checkout -b "$branch_name" origin/stable 25 | # Go ahead and put the branch out on the server 26 | git push origin "$branch_name" 27 | # Setup the merge property so that pulls come from the right place (instead of stable) 28 | git config --replace-all "branch.$branch_name.merge" "refs/heads/$branch_name" 29 | else 30 | # Do the checkout 31 | git checkout -b "$branch_name" "origin/$branch_name" 32 | fi 33 | fi 34 | 35 | -------------------------------------------------------------------------------- /scripts/create-gitconfig: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Creates a separate DAG in your repo for git config files. 4 | # 5 | # This allows you to checkout/modify/push the config/hooks for your repo 6 | # locally. Then, with the help of post-receive-gitconfig, have your changes be 7 | # automatically updated on the server. 8 | # 9 | # Also, it keeps all of your repo-specific information in your repo instead of 10 | # leaking out into potentiallyshared hook scripts. 11 | # 12 | 13 | # Create an empty file object 14 | empty_file_hash=$(git hash-object -w --stdin <.rebase flag, it will replay 10 | # commits if you have local merges of other branches that are being rebased--the 11 | # "-p" parameter to git rebase prevents this reply but is not available from the 12 | # "git pull" command line or config options--hence this script. 13 | # 14 | 15 | branch_name=$(git symbolic-ref --quiet HEAD) 16 | if [[ $? -ne 0 ]] ; then 17 | echo "Not on a branch" 18 | exit 1 19 | fi 20 | branch_name=${branch_name/refs\/heads\//} 21 | 22 | git fetch 23 | if [[ $? -ne 0 ]] ; then 24 | exit $? 25 | fi 26 | 27 | # rebase-p-i stops if nothing to do, even a ff, so do a non-i-p if needed 28 | if test "$(git rev-parse HEAD)" = "$(git merge-base HEAD origin/$branch_name)" ; then 29 | git rebase "origin/$branch_name" 30 | else 31 | GIT_EDITOR=: git rebase -p -i "origin/$branch_name" 32 | fi 33 | 34 | -------------------------------------------------------------------------------- /scripts/push: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Makes push "just work" by only pushing the current branch to origin. 4 | # 5 | 6 | branch_name=$(git symbolic-ref --quiet HEAD) 7 | if [[ $? -ne 0 ]] ; then 8 | echo "Not on a branch" 9 | exit 1 10 | fi 11 | 12 | branch_name=${branch_name/refs\/heads\//} 13 | 14 | git push origin $branch_name 15 | if [[ $? -ne 0 ]] ; then 16 | exit $? 17 | fi 18 | 19 | # ...tags... 20 | 21 | -------------------------------------------------------------------------------- /scripts/refollow: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 4 | # Goes through the ensure-follows branches and makes sure 5 | # the non-excused branches have it merged. 6 | # 7 | 8 | die() { 9 | echo >&2 "$@" 10 | exit 1 11 | } 12 | 13 | attempt_merge() { 14 | source_branch=$1 15 | remote_branch=$2 16 | 17 | if [ "$source_branch" == "$remote_branch" ] ; then 18 | return 19 | fi 20 | if [[ $excused =~ " $remote_branch " ]] ; then 21 | return 22 | fi 23 | git branch -r --contains "$source_branch" | grep --quiet "origin/$remote_branch" 24 | if [ $? -eq 0 ] ; then 25 | return 26 | fi 27 | 28 | echo -n "Merging $source_branch into $remote_branch..." 29 | 30 | baserev=$(git merge-base "$source_branch" "origin/$remote_branch") 31 | git read-tree -m --trivial $baserev "origin/$remote_branch" "$source_branch" 32 | if [ $? -ne 0 ] ; then 33 | echo "failed merge" 34 | return 35 | fi 36 | 37 | new_tree=$(git write-tree) 38 | new_commit=$(git commit-tree $new_tree -p "origin/$remote_branch" -p "$source_branch" </dev/null 2>/dev/null 47 | if [ $? -ne 0 ] ; then 48 | echo "failed push" 49 | return 50 | fi 51 | 52 | echo "succeeded" 53 | } 54 | 55 | gitconfig=$(git rev-parse origin/gitconfig) 56 | if [ $? -ne 0 ] ; then 57 | echo "gitconfig branch not found" 58 | exit 1 59 | fi 60 | 61 | # The source branches 62 | config_hash=$(git ls-tree $gitconfig | grep config | grep -oP '\w{40}') 63 | branches=$(git cat-file blob "$config_hash" | grep hooks.update-ensure-follows.branches) 64 | branches=("${branches#*=}") 65 | excused=$(git cat-file blob "$config_hash" | grep hooks.update-ensure-follows.excused) 66 | excused=" ${excused#*=} " 67 | 68 | # We're going to merge stuff into the index, so make sure it's okay 69 | git diff-index --cached --quiet HEAD -- || die "refusing to refollow--your index is not clean" 70 | 71 | # Get the latest remote refs 72 | git fetch 73 | 74 | # So we can put the index back after we screw with it 75 | original_head=$(git rev-parse HEAD) 76 | 77 | for source_branch in ${branches[@]} ; do 78 | git branch -r | grep -v HEAD | while read line ; do 79 | if [[ "$line" =~ origin/(.*) ]] ; then 80 | attempt_merge $source_branch ${BASH_REMATCH[1]} 81 | fi 82 | done 83 | done 84 | 85 | # Put the index back 86 | git read-tree "$original_head" 87 | 88 | -------------------------------------------------------------------------------- /server/functions: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Sets: new_commits 4 | # Assumes: $oldrev $newrev $refname 5 | # 6 | # This is for use in post receive hooks, as it assumes the refname has moved and 7 | # is now newrev, we need to discard it. This is down with bash string replace, 8 | # as it will replace only the first match, compared to the canonical "grep -v" 9 | # approach which will throw out multiple matches if the same commit is referred 10 | # to by multiple branches. 11 | # 12 | # Excellent, excellent docs from Andy Parkin's email script 13 | # 14 | ################################################## 15 | # 16 | # Consider this: 17 | # 1 --- 2 --- O --- X --- 3 --- 4 --- N 18 | # 19 | # O is $oldrev for $refname 20 | # N is $newrev for $refname 21 | # X is a revision pointed to by some other ref, for which we may 22 | # assume that an email has already been generated. 23 | # In this case we want to issue an email containing only revisions 24 | # 3, 4, and N. Given (almost) by 25 | # 26 | # git rev-list N ^O --not --all 27 | # 28 | # The reason for the "almost", is that the "--not --all" will take 29 | # precedence over the "N", and effectively will translate to 30 | # 31 | # git rev-list N ^O ^X ^N 32 | # 33 | # So, we need to build up the list more carefully. git rev-parse 34 | # will generate a list of revs that may be fed into git rev-list. 35 | # We can get it to make the "--not --all" part and then filter out 36 | # the "^N" with: 37 | # 38 | # git rev-parse --not --all | grep -v N 39 | # 40 | # Then, using the --stdin switch to git rev-list we have effectively 41 | # manufactured 42 | # 43 | # git rev-list N ^O ^X 44 | # 45 | # This leaves a problem when someone else updates the repository 46 | # while this script is running. Their new value of the ref we're 47 | # working on would be included in the "--not --all" output; and as 48 | # our $newrev would be an ancestor of that commit, it would exclude 49 | # all of our commits. What we really want is to exclude the current 50 | # value of $refname from the --not list, rather than N itself. So: 51 | # 52 | # git rev-parse --not --all | grep -v $(git rev-parse $refname) 53 | # 54 | # Get's us to something pretty safe (apart from the small time 55 | # between refname being read, and git rev-parse running - for that, 56 | # I give up) 57 | # 58 | # 59 | # Next problem, consider this: 60 | # * --- B --- * --- O ($oldrev) 61 | # \ 62 | # * --- X --- * --- N ($newrev) 63 | # 64 | # That is to say, there is no guarantee that oldrev is a strict 65 | # subset of newrev (it would have required a --force, but that's 66 | # allowed). So, we can't simply say rev-list $oldrev..$newrev. 67 | # Instead we find the common base of the two revs and list from 68 | # there. 69 | # 70 | # As above, we need to take into account the presence of X; if 71 | # another branch is already in the repository and points at some of 72 | # the revisions that we are about to output - we don't want them. 73 | # The solution is as before: git rev-parse output filtered. 74 | # 75 | # Finally, tags: 1 --- 2 --- O --- T --- 3 --- 4 --- N 76 | # 77 | # Tags pushed into the repository generate nice shortlog emails that 78 | # summarise the commits between them and the previous tag. However, 79 | # those emails don't include the full commit messages that we output 80 | # for a branch update. Therefore we still want to output revisions 81 | # that have been output on a tag email. 82 | # 83 | # Luckily, git rev-parse includes just the tool. Instead of using 84 | # "--all" we use "--branches"; this has the added benefit that 85 | # "remotes/" will be ignored as well. 86 | # 87 | ################################################## 88 | set_new_commits() { 89 | nl=$'\n' 90 | 91 | # Get all the current branches, not'd as we want only new ones 92 | new_commits=$(git rev-parse --not --branches) 93 | 94 | # Strip off the not current branch 95 | new_hash=$(git rev-parse $refname) 96 | new_commits=${new_commits/^$new_hash/} 97 | 98 | # Put back newrev without the not 99 | new_commits=${new_commits}${nl}${newrev} 100 | 101 | # Put in ^oldrev if it's not a new branch 102 | if [ "$oldrev" != "0000000000000000000000000000000000000000" ] ; then 103 | new_commits=${new_commits}${nl}^${oldrev} 104 | fi 105 | 106 | new_commits=${new_commits/$nl$nl/$nl} 107 | new_commits=${new_commits/#$nl/} 108 | } 109 | 110 | # Sets: $change_type 111 | # Assumes: $oldrev $newrev 112 | # 113 | # --- Interpret 114 | # 0000->1234 (create) 115 | # 1234->2345 (update) 116 | # 2345->0000 (delete) 117 | set_change_type() { 118 | if [ "$oldrev" == "0000000000000000000000000000000000000000" ] ; then 119 | change_type="create" 120 | else 121 | if [ "$newrev" == "0000000000000000000000000000000000000000" ] ; then 122 | change_type="delete" 123 | else 124 | change_type="update" 125 | fi 126 | fi 127 | } 128 | 129 | # Sets: $newrev_type $oldrev_type $rev $rev_type 130 | # Assumes: $newrev $oldrev 131 | # --- Get the revision types 132 | set_rev_types() { 133 | newrev_type=$(git cat-file -t "$newrev" 2> /dev/null) 134 | oldrev_type=$(git cat-file -t "$oldrev" 2> /dev/null) 135 | if [ "$newrev" == "0000000000000000000000000000000000000000" ] ; then 136 | rev_type="$oldrev_type" 137 | rev="$oldrev" 138 | else 139 | rev_type="$newrev_type" 140 | rev="$newrev" 141 | fi 142 | } 143 | 144 | # Sets: $describe 145 | # Assumes: $rev 146 | # 147 | # The email subject will contain the best description of the ref that we can build from the parameters 148 | set_describe() { 149 | rev_to_describe="$rev" 150 | if [ "$1" != "" ] ; then 151 | rev_to_describe="$1" 152 | fi 153 | 154 | describe=$(git describe $rev_to_describe 2>/dev/null) 155 | if [ -z "$describe" ]; then 156 | describe=$rev_to_describe 157 | fi 158 | } 159 | 160 | # Sets: $describe_tags 161 | # Assumes: $rev 162 | # 163 | # The email subject will contain the best description of the ref that we can build from the parameters 164 | set_describe_tags() { 165 | rev_to_describe="$rev" 166 | if [ "$1" != "" ] ; then 167 | rev_to_describe="$1" 168 | fi 169 | 170 | describe_tags=$(git describe --tags $rev_to_describe 2>/dev/null) 171 | if [ -z "$describe_tags" ]; then 172 | describe_tags=$rev_to_describe 173 | fi 174 | } 175 | 176 | # Takes a lockfile path and command to execute once the lock is acquired. 177 | # 178 | # Retries every second for 5 minutes. 179 | # 180 | # with_lock "foo.lock" "echo a" # Works 181 | # with_lock "foo.lock" "echo b1 ; echo b2" # Work 182 | # several() { 183 | # echo several1 ; echo several2 184 | # } 185 | # with_lock "foo.lock" several # Works 186 | # 187 | with_lock() { 188 | lockfile="$1" 189 | block="$2" 190 | with_lock_has_lock=1 191 | 192 | trap with_lock_unlock_if_held INT TERM EXIT 193 | # used to use lockfile to try multiple times but it's not always available 194 | mkdir "$lockfile" 195 | with_lock_has_lock=$? 196 | if [ $with_lock_has_lock -ne 0 ] ; then 197 | exit $? 198 | fi 199 | eval "$block" 200 | rmdir "$lockfile" 201 | with_lock_has_lock=1 202 | } 203 | 204 | with_lock_unlock_if_held() { 205 | if [[ "$with_lock_has_lock" -eq 0 ]] ; then 206 | rm -f "$lockfile" 207 | fi 208 | } 209 | 210 | display_error_message() { 211 | echo "----------------------------------------------------" >&2 212 | echo "" >&2 213 | echo "" >&2 214 | for ((i=1;i<=$#;i+=1)); do 215 | eval message="$"$i 216 | echo "$message" >&2 217 | done 218 | echo "" >&2 219 | echo "" >&2 220 | echo "----------------------------------------------------" >&2 221 | } 222 | 223 | -------------------------------------------------------------------------------- /server/post-receive-commitnumbers: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Creates commitnumbers as lightweight tags named "r/X" where X increases 4 | # monotonically. 5 | # 6 | # Works by creating a GIT_DIR/commitnumbers file that is a list of all 7 | # commit SHA1s where the commitnumber == the line number of the SHA1 in the 8 | # file. 9 | # 10 | # If you're adding commitnumbers to an existing repo, you can jump start it 11 | # (without the tags, but so you don't start at 0), by: 12 | # 13 | # git rev-list --all > $GIT_DIR/commitnumbers 14 | # 15 | # There is no real reason the tags are named "r/X"--feel free to substitute your 16 | # own prefix or drop it all together. That should probably be a config variable. 17 | # 18 | 19 | . $(dirname $0)/functions 20 | 21 | while read oldrev newrev refname ; do 22 | set_new_commits 23 | echo "$new_commits" | git rev-list --reverse --stdin | while read commit ; do 24 | if [[ $(grep "$commit" "$GIT_DIR/commitnumbers" 2>/dev/null) == "" ]] ; then 25 | with_lock "$GIT_DIR/commitnumbers.lock" 'echo "$commit $refname" >> "$GIT_DIR/commitnumbers"' 26 | number=$(grep --max-count=1 --line-number "$commit" "$GIT_DIR/commitnumbers" | grep -oP "^\d+(?=:)") 27 | git tag "r/$number" "$commit" 28 | fi 29 | done 30 | done 31 | 32 | -------------------------------------------------------------------------------- /server/post-receive-email: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Copyright (c) 2007 Andy Parkins 4 | # Copyright (c) 2008 Stephen Haberman 5 | # 6 | # This hook sends emails listing new revisions to the repository introduced by 7 | # the change being reported. The rule is that (for branch updates) each commit 8 | # will appear on one email and one email only. 9 | # 10 | # Differences from the contrib script (off the top of my head): 11 | # 12 | # * Sends combined diff output which is great for viewing merge commits 13 | # * Changes order of commit listing to be oldest to newest 14 | # * Configurable sendmail path 15 | # * Use git describe --tags for the email subject to pick up commitnumbers 16 | # 17 | # Config 18 | # ------ 19 | # hooks.post-receive-email.mailinglist 20 | # This is the list that all pushes will go to; leave it blank to not send 21 | # emails for every ref update. 22 | # hooks.post-receive-email.announcelist 23 | # This is the list that all pushes of annotated tags will go to. Leave it 24 | # blank to default to the mailinglist field. The announce emails lists 25 | # the short log summary of the changes since the last annotated tag. 26 | # hooks.post-receive-email.envelopesender 27 | # If set then the -f option is passed to sendmail to allow the envelope 28 | # sender address to be set 29 | # hooks.post-receive-email.sendmail 30 | # The path to sendmail, e.g. /usr/sbin/sendmail or /bin/msmtp 31 | # USER_EMAIL 32 | # Environment variable that should be set by your repository-specific 33 | # post-receive hook. E.g. export USER_EMAIL=${USER}@example.com 34 | # 35 | # Notes 36 | # ----- 37 | # All emails include the headers "X-Git-Refname", "X-Git-Oldrev", 38 | # "X-Git-Newrev", and "X-Git-Reftype" to enable fine tuned filtering and 39 | # give information for debugging. 40 | # 41 | 42 | # ---------------------------- Functions 43 | 44 | . $(dirname $0)/functions 45 | 46 | # 47 | # Top level email generation function. This decides what type of update 48 | # this is and calls the appropriate body-generation routine after outputting 49 | # the common header 50 | # 51 | # Note this function doesn't actually generate any email output, that is 52 | # taken care of by the functions it calls: 53 | # - generate_email_header 54 | # - generate_create_XXXX_email 55 | # - generate_update_XXXX_email 56 | # - generate_delete_XXXX_email 57 | # 58 | generate_email() 59 | { 60 | # --- Arguments 61 | oldrev=$(git rev-parse $1) 62 | newrev=$(git rev-parse $2) 63 | refname="$3" 64 | 65 | set_change_type 66 | set_rev_types 67 | set_describe_tags 68 | 69 | # The revision type tells us what type the commit is, combined with 70 | # the location of the ref we can decide between 71 | # - working branch 72 | # - tracking branch 73 | # - unannoted tag 74 | # - annotated tag 75 | case "$refname","$rev_type" in 76 | refs/tags/*,commit) 77 | # un-annotated tag 78 | refname_type="tag" 79 | function="ltag" 80 | short_refname=${refname##refs/tags/} 81 | ;; 82 | refs/tags/*,tag) 83 | # annotated tag 84 | refname_type="annotated tag" 85 | function="atag" 86 | short_refname=${refname##refs/tags/} 87 | # change recipients 88 | if [ -n "$announcerecipients" ]; then 89 | recipients="$announcerecipients" 90 | fi 91 | ;; 92 | refs/heads/*,commit) 93 | # branch 94 | refname_type="branch" 95 | function="branch" 96 | short_refname=${refname##refs/heads/} 97 | ;; 98 | refs/remotes/*,commit) 99 | # tracking branch 100 | refname_type="tracking branch" 101 | short_refname=${refname##refs/remotes/} 102 | echo >&2 "*** Push-update of tracking branch, $refname" 103 | echo >&2 "*** - no email generated." 104 | exit 0 105 | ;; 106 | *) 107 | # Anything else (is there anything else?) 108 | echo >&2 "*** Unknown type of update to $refname ($rev_type)" 109 | echo >&2 "*** - no email generated" 110 | exit 1 111 | ;; 112 | esac 113 | 114 | # Check if we've got anyone to send to 115 | if [ -z "$recipients" ]; then 116 | case "$refname_type" in 117 | "annotated tag") 118 | config_name="hooks.post-receive-email.announcelist" 119 | ;; 120 | *) 121 | config_name="hooks.post-receive-email.mailinglist" 122 | ;; 123 | esac 124 | echo >&2 "*** $config_name is not set so no email will be sent" 125 | echo >&2 "*** for $refname update $oldrev->$newrev" 126 | exit 0 127 | fi 128 | 129 | generate_email_header 130 | generate_${change_type}_${function}_email 131 | } 132 | 133 | generate_email_header() 134 | { 135 | # --- Email (all stdout will be the email) 136 | # Generate header 137 | cat <<-EOF 138 | From: $USER_EMAIL 139 | To: $recipients 140 | Subject: ${emailprefix} $short_refname $refname_type ${change_type}d. $describe_tags 141 | X-Git-Refname: $refname 142 | X-Git-Reftype: $refname_type 143 | X-Git-Oldrev: $oldrev 144 | X-Git-Newrev: $newrev 145 | 146 | The $refname_type, $short_refname has been ${change_type}d 147 | EOF 148 | } 149 | 150 | 151 | # --------------- Branches 152 | 153 | # 154 | # Called for the creation of a branch 155 | # 156 | generate_create_branch_email() 157 | { 158 | # This is a new branch and so oldrev is not valid 159 | git rev-list --pretty=format:" at %h %s" --no-walk "$newrev" | grep -vP "^commit" 160 | 161 | set_new_commits 162 | 163 | echo "" 164 | echo $LOGBEGIN 165 | echo "$new_commits" | git rev-list --reverse --stdin | while read commit ; do 166 | echo "" 167 | git rev-list --no-walk --pretty "$commit" 168 | git diff-tree --cc "$commit" 169 | echo "" 170 | echo $LOGEND 171 | done 172 | 173 | oldest_new=$(echo "$new_commits" | git rev-list --stdin | tail -n 1) 174 | if [ "$oldest_new" != "" ] ; then 175 | echo "" 176 | echo "Summary of changes:" 177 | git diff-tree --stat $oldest_new^..$newrev 178 | fi 179 | } 180 | 181 | # 182 | # Called for the change of a pre-existing branch 183 | # 184 | generate_update_branch_email() 185 | { 186 | # List all of the revisions that were removed by this update (hopefully empty) 187 | git rev-list --first-parent --pretty=format:" discards %h %s" $newrev..$oldrev | grep -vP "^commit" 188 | 189 | # List all of the revisions that were added by this update 190 | git rev-list --first-parent --pretty=format:" via %h %s" $oldrev..$newrev | grep -vP "^commit" 191 | 192 | removed=$(git rev-list $newrev..$oldrev) 193 | if [ "$removed" == "" ] ; then 194 | git rev-list --no-walk --pretty=format:" from %h %s" $oldrev | grep -vP "^commit" 195 | else 196 | # Must be rewind, could be rewind+addition 197 | echo "" 198 | 199 | # Find the common ancestor of the old and new revisions and compare it with newrev 200 | baserev=$(git merge-base $oldrev $newrev) 201 | rewind_only="" 202 | if [ "$baserev" = "$newrev" ]; then 203 | echo "This update discarded existing revisions and left the branch pointing at" 204 | echo "a previous point in the repository history." 205 | echo "" 206 | echo " * -- * -- N ($newrev)" 207 | echo " \\" 208 | echo " O -- O -- O ($oldrev)" 209 | echo "" 210 | echo "The removed revisions are not necessarilly gone - if another reference" 211 | echo "still refers to them they will stay in the repository." 212 | rewind_only=1 213 | else 214 | echo "This update added new revisions after undoing existing revisions. That is" 215 | echo "to say, the old revision is not a strict subset of the new revision. This" 216 | echo "situation occurs when you --force push a change and generate a repository" 217 | echo "containing something like this:" 218 | echo "" 219 | echo " * -- * -- B -- O -- O -- O ($oldrev)" 220 | echo " \\" 221 | echo " N -- N -- N ($newrev)" 222 | echo "" 223 | echo "When this happens we assume that you've already had alert emails for all" 224 | echo "of the O revisions, and so we here report only the revisions in the N" 225 | echo "branch from the common base, B." 226 | fi 227 | fi 228 | 229 | echo "" 230 | if [ -z "$rewind_only" ]; then 231 | echo "Those revisions listed above that are new to this repository have" 232 | echo "not appeared on any other notification email; so we list those" 233 | echo "revisions in full, below." 234 | 235 | set_new_commits 236 | 237 | echo "" 238 | echo $LOGBEGIN 239 | echo "$new_commits" | git rev-list --reverse --stdin | while read commit ; do 240 | echo "" 241 | git rev-list --no-walk --pretty "$commit" 242 | git diff-tree --cc "$commit" 243 | echo "" 244 | echo $LOGEND 245 | done 246 | 247 | # XXX: Need a way of detecting whether git rev-list actually 248 | # outputted anything, so that we can issue a "no new 249 | # revisions added by this update" message 250 | else 251 | echo "No new revisions were added by this update." 252 | fi 253 | 254 | # Show the diffstat which is what really happened (new commits/whatever aside) 255 | echo "" 256 | echo "Summary of changes:" 257 | git diff-tree --stat --find-copies-harder $oldrev..$newrev 258 | } 259 | 260 | # 261 | # Called for the deletion of a branch 262 | # 263 | generate_delete_branch_email() 264 | { 265 | echo " was $oldrev" 266 | echo "" 267 | echo $LOGEND 268 | git show -s --pretty=oneline $oldrev 269 | echo $LOGEND 270 | } 271 | 272 | # --------------- Annotated tags 273 | 274 | # 275 | # Called for the creation of an annotated tag 276 | # 277 | generate_create_atag_email() 278 | { 279 | echo " at $newrev ($newrev_type)" 280 | generate_atag_email 281 | } 282 | 283 | # 284 | # Called for the update of an annotated tag (this is probably a rare event 285 | # and may not even be allowed) 286 | # 287 | generate_update_atag_email() 288 | { 289 | echo " to $newrev ($newrev_type)" 290 | echo " from $oldrev (which is now obsolete)" 291 | generate_atag_email 292 | } 293 | 294 | # 295 | # Called when an annotated tag is created or changed 296 | # 297 | generate_atag_email() 298 | { 299 | # Use git for-each-ref to pull out the individual fields from the 300 | # tag 301 | eval $(git for-each-ref --shell --format=' 302 | tagobject=%(*objectname) 303 | tagtype=%(*objecttype) 304 | tagger=%(taggername) 305 | tagged=%(taggerdate)' $refname 306 | ) 307 | 308 | echo " tagging $tagobject ($tagtype)" 309 | case "$tagtype" in 310 | commit) 311 | # If the tagged object is a commit, then we assume this is a 312 | # release, and so we calculate which tag this tag is replacing 313 | prevtag=$(git describe --abbrev=0 $newrev^ 2>/dev/null) 314 | if [ -n "$prevtag" ]; then 315 | echo " replaces $prevtag" 316 | fi 317 | ;; 318 | *) 319 | echo " length $(git cat-file -s $tagobject) bytes" 320 | ;; 321 | esac 322 | echo " tagged by $tagger" 323 | echo " on $tagged" 324 | 325 | echo "" 326 | echo $LOGBEGIN 327 | 328 | # Show the content of the tag message; this might contain a change 329 | # log or release notes so is worth displaying. 330 | git cat-file tag $newrev | sed -e '1,/^$/d' 331 | 332 | echo "" 333 | case "$tagtype" in 334 | commit) 335 | # Only commit tags make sense to have rev-list operations 336 | # performed on them 337 | if [ -n "$prevtag" ]; then 338 | # Show changes since the previous release 339 | git rev-list --pretty=short "$prevtag..$newrev" | git shortlog 340 | else 341 | # No previous tag, show all the changes since time 342 | # began 343 | git rev-list --pretty=short $newrev | git shortlog 344 | fi 345 | ;; 346 | *) 347 | # XXX: Is there anything useful we can do for non-commit 348 | # objects? 349 | ;; 350 | esac 351 | 352 | echo $LOGEND 353 | } 354 | 355 | # 356 | # Called for the deletion of an annotated tag 357 | # 358 | generate_delete_atag_email() 359 | { 360 | echo " was $oldrev ($oldrev_type)" 361 | echo "" 362 | echo $LOGEND 363 | git show -s --pretty=oneline $oldrev 364 | echo $LOGEND 365 | } 366 | 367 | # --------------- General references 368 | 369 | # 370 | # Called when any other type of reference is created (most likely a 371 | # non-annotated tag) 372 | # 373 | generate_create_ltag_email() 374 | { 375 | echo " at $newrev ($newrev_type)" 376 | generate_ltag_email 377 | } 378 | 379 | # 380 | # Called when any other type of reference is updated (most likely a 381 | # non-annotated tag) 382 | # 383 | generate_update_ltag_email() 384 | { 385 | echo " to $newrev ($newrev_type)" 386 | echo " from $oldrev ($oldrev_type)" 387 | generate_ltag_email 388 | } 389 | 390 | # 391 | # Called for creation or update of any other type of reference 392 | # 393 | generate_ltag_email() 394 | { 395 | # Unannotated tags are more about marking a point than releasing a 396 | # version; therefore we don't do the shortlog summary that we do for 397 | # annotated tags above - we simply show that the point has been 398 | # marked, and print the log message for the marked point for 399 | # reference purposes 400 | # 401 | # Note this section also catches any other reference type (although 402 | # there aren't any) and deals with them in the same way. 403 | 404 | echo "" 405 | if [ "$newrev_type" = "commit" ]; then 406 | echo $LOGBEGIN 407 | git show --no-color --root -s --pretty=medium $newrev 408 | echo $LOGEND 409 | else 410 | # What can we do here? The tag marks an object that is not 411 | # a commit, so there is no log for us to display. It's 412 | # probably not wise to output git cat-file as it could be a 413 | # binary blob. We'll just say how big it is 414 | echo "$newrev is a $newrev_type, and is $(git cat-file -s $newrev) bytes long." 415 | fi 416 | } 417 | 418 | # 419 | # Called for the deletion of any other type of reference 420 | # 421 | generate_delete_ltag_email() 422 | { 423 | echo " was $oldrev ($oldrev_type)" 424 | echo "" 425 | echo $LOGEND 426 | git show -s --pretty=oneline $oldrev 427 | echo $LOGEND 428 | } 429 | 430 | send_mail() 431 | { 432 | if [ -n "$envelopesender" ] ; then 433 | $sendmail -t -f "$envelopesender" 434 | else 435 | $sendmail -t 436 | fi 437 | } 438 | 439 | # ---------------------------- main() 440 | 441 | # --- Constants 442 | LOGBEGIN="- Log -----------------------------------------------------------------" 443 | LOGEND="-----------------------------------------------------------------------" 444 | 445 | # --- Config 446 | # Set GIT_DIR either from the working directory or the environment variable. 447 | GIT_DIR=$(git rev-parse --git-dir 2>/dev/null) 448 | if [ -z "$GIT_DIR" ]; then 449 | echo >&2 "fatal: post-receive: GIT_DIR not set" 450 | exit 1 451 | fi 452 | 453 | projectdesc=$(sed -ne '1p' "$GIT_DIR/description") 454 | # Shorten the description if it's the default 455 | if expr "$projectdesc" : "Unnamed repository.*$" >/dev/null ; then 456 | projectdesc="UNNAMED" 457 | fi 458 | 459 | recipients=$(git config hooks.post-receive-email.mailinglist) 460 | announcerecipients=$(git config hooks.post-receive-email.announcelist) 461 | envelopesender=$(git config hooks.post-receive-email.envelopesender) 462 | emailprefix="[$projectdesc]" 463 | debug=$(git config hooks.post-receive-email.debug) 464 | sendmail=$(git config hooks.post-receive-email.sendmail) 465 | 466 | # --- Main loop 467 | # Allow dual mode: run from the command line just like the update hook, or 468 | # if no arguments are given then run as a hook script 469 | if [ -n "$1" -a -n "$2" -a -n "$3" ]; then 470 | # Output to the terminal in command line mode - if someone wanted to 471 | # resend an email; they could redirect the output to sendmail 472 | # themselves 473 | PAGER= generate_email $2 $3 $1 474 | else 475 | while read oldrev newrev refname 476 | do 477 | if [ "$debug" == "true" ] ; then 478 | generate_email $oldrev $newrev $refname > "${refname//\//.}.out" 479 | else 480 | generate_email $oldrev $newrev $refname | send_mail 481 | fi 482 | done 483 | fi 484 | 485 | -------------------------------------------------------------------------------- /server/post-receive-gitconfig: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Copyright (c) 2008 Stephen Haberman 4 | # 5 | # Auto-deploys the contents of the gitconfig branch to $GIT_DIR. 6 | # 7 | # The config file is handled specially--instead of the $GIT_DIR's config file 8 | # being copied over entirely, the gitconfig:config is evaluated line by line 9 | # with calls to `git config`. This means you can have settings in 10 | # $GIT_DIR/config that are not in the gitconfig:config and they will not be 11 | # overwritten. Deleting $GIT_DIR/config entries has to be manually. 12 | # 13 | # Hooks are copied over entirely, but old ones are not deleted. Deleting 14 | # existing hooks has to be done manually. 15 | # 16 | 17 | while read oldrev newrev refname ; do 18 | if [ "$refname" == "refs/heads/gitconfig" ] ; then 19 | config_hash=$(git ls-tree $newrev | grep config | grep -oP '\w{40}') 20 | if [[ "$config_hash" != "" ]] ; then 21 | git cat-file blob "$config_hash" | while read line ; do 22 | key="${line%=*}" 23 | value="${line#*=}" 24 | git config "${key}" "${value}" 25 | done 26 | fi 27 | 28 | hooks_hash=$(git ls-tree $newrev | grep hooks | grep -oP '\w{40}') 29 | if [[ "$hooks_hash" != "" ]] ; then 30 | git ls-tree "$hooks_hash" | while read mode type file_hash file_name ; do 31 | echo "Installing $file_name" 32 | git cat-file blob "$file_hash" > "hooks/$file_name" 33 | done 34 | fi 35 | 36 | git ls-tree $newrev | grep -v hooks | grep -v config | grep blob | while read mode type file_hash file_name ; do 37 | echo "Installing $file_name" 38 | git cat-file blob "$file_hash" > "$file_name" 39 | done 40 | fi 41 | done 42 | 43 | -------------------------------------------------------------------------------- /server/post-receive-hudson: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Copyright (c) 2008 Stephen Haberman 4 | # 5 | # This hook creates new jobs for each branch in the Hudson continuous 6 | # integration tool. Besides creating the job if needed, the user who pushed is 7 | # added to the job's email list if they were not already there. 8 | # 9 | # Config 10 | # ------ 11 | # hooks.post-receive-hudson.url 12 | # The url to hudson, e.g. http://internalbox/hudson 13 | # hooks.post-receive-hudson.ignored 14 | # Whitespace separated list of branches to not make jobs for. 15 | # USER_EMAIL 16 | # Environment variable that should be set by your repository-specific 17 | # post-receive hook. E.g. export USER_EMAIL=${USER}@example.com. If 18 | # unset, defaults to the email by of the pushed commit. 19 | # 20 | 21 | . $(dirname $0)/functions 22 | 23 | while read oldrev newrev refname ; do 24 | case "$refname" in 25 | refs/tags/*) 26 | exit 0 27 | ;; 28 | refs/heads/*) 29 | short_refname=${refname##refs/heads/} 30 | ;; 31 | *) 32 | echo >&2 "*** Unknown type of update to $refname" 33 | exit 1 34 | ;; 35 | esac 36 | 37 | ignored=" $(git config hooks.post-receive-hudson.ignored) " 38 | hudson_url=$(git config hooks.post-receive-hudson.url) 39 | if [[ $ignored =~ " $short_refname " ]] ; then 40 | exit 0 41 | fi 42 | 43 | if [ -z "$USER_EMAIL" ] ; then 44 | USER_EMAIL=$(git log -1 --pretty=format:'%ce' $newrev) 45 | fi 46 | 47 | branch_config=$(wget -O - $hudson_url/job/${short_refname}/config.xml 2>/dev/null) 48 | if [ $? -ne 0 ] ; then 49 | # Create the job 50 | stable_config=$(wget -O - $hudson_url/job/stable/config.xml 2>/dev/null) 51 | if [ $? -ne 0 ] ; then 52 | display_error_message "Could not get existing Hudson config for ${short_refname}" 53 | exit 0 54 | fi 55 | 56 | # Replace stable with our branch 57 | branch_config="${stable_config/stable$short_refname<}" 58 | 59 | # Add email to recipients list 60 | if [ "${branch_config/$USER_EMAIL/}" == "$branch_config" ] ; then 61 | branch_config="${branch_config//$USER_EMAIL }" 62 | fi 63 | 64 | # Make the new job 65 | wget --header "Content-Type: text/xml" --post-data="$branch_config" -O - "$hudson_url/createItem?name=${short_refname}" >/dev/null 2>/dev/null 66 | if [ $? -ne 0 ] ; then 67 | display_error_message "Could not create new Hudson job for ${short_refname}" 68 | exit 0 69 | fi 70 | else 71 | # Add email to recipients list 72 | if [ "${branch_config/$USER_EMAIL/}" == "$branch_config" ] ; then 73 | branch_config="${branch_config//$USER_EMAIL }" 74 | 75 | # Update the config 76 | wget --header "Content-Type: text/xml" --post-data="$branch_config" -O - "$hudson_url/job/${short_refname}/config.xml" >/dev/null 2>/dev/null 77 | if [ $? -ne 0 ] ; then 78 | display_error_message "Could not add $USER_EMAIL to Hudson job ${short_refname}" 79 | exit 0 80 | fi 81 | fi 82 | fi 83 | done 84 | 85 | exit 0 86 | 87 | -------------------------------------------------------------------------------- /server/post-receive-trac: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | . $(dirname $0)/functions 4 | 5 | while read oldrev newrev refname ; do 6 | case "$refname" in 7 | refs/tags/*) 8 | short_refname=${refname##refs/tags/} 9 | ;; 10 | refs/heads/*) 11 | short_refname=${refname##refs/heads/} 12 | ;; 13 | *) 14 | echo >&2 "*** Unknown type of update to $refname" 15 | exit 1 16 | ;; 17 | esac 18 | 19 | set_new_commits 20 | set_rev_types 21 | 22 | echo "$new_commits" | git rev-list --reverse --stdin | while read commit ; do 23 | set_describe "$commit" 24 | set_describe_tags "$commit" 25 | author="$(git rev-list -n 1 $commit --pretty=format:'%cn <%ce>' | grep -v $commit)" 26 | message="$(git rev-list -n 1 $commit --pretty=format:'%s%n%b' | grep -v $commit)" 27 | "$PYTHON" "$(dirname $0)/post-receive-trac.py" "$TRAC_ENV" "$short_refname" "$describe" "$describe_tags" "$commit" "$author" "$message" 28 | # Trac will crap out if we submit ticket refs too quickly 29 | # http://trac.edgewall.org/ticket/6466 30 | sleep 1 31 | done 32 | done 33 | 34 | exit 0 35 | 36 | -------------------------------------------------------------------------------- /server/post-receive-trac.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # This should work: 4 | # 5 | # Changed blah and foo to do this or that. Re #10 and #12, and qa #12. 6 | # 7 | 8 | import re 9 | import sys 10 | from datetime import datetime 11 | 12 | from trac.env import open_environment 13 | from trac.ticket.notification import TicketNotifyEmail 14 | from trac.ticket import Ticket 15 | from trac.ticket.web_ui import TicketModule 16 | from trac.util.datefmt import utc 17 | from trac.versioncontrol.api import NoSuchChangeset 18 | 19 | project = sys.argv[1] 20 | refname = sys.argv[2] 21 | describe = sys.argv[3] 22 | describe_tags = sys.argv[4] 23 | rev = sys.argv[5] 24 | author = sys.argv[6] 25 | message = sys.argv[7] 26 | 27 | def refs(ticket): 28 | pass 29 | 30 | def qa(ticket): 31 | if ticket['phase'] == 'Final Fixing': 32 | ticket['phase'] = 'Final QA' 33 | else: 34 | ticket['phase'] = 'Initial QA' 35 | ticket['owner'] = '' 36 | ticket['status'] = 'new' 37 | 38 | commands = { 're': refs, 'refs': refs, 'qa': qa } 39 | commandPattern = re.compile(r'(?P[A-Za-z]*).?(?P#[0-9]+(?:(?:[, &]*|[ ]?and[ ]?)#[0-9]+)*)') 40 | ticketPattern = re.compile(r'#([0-9]*)') 41 | authorPattern = re.compile(r'<(.+)@') 42 | tickets = {} 43 | 44 | env = open_environment(project) 45 | for command, ticketList in commandPattern.findall(message): 46 | if commands.has_key(command.lower()): 47 | for ticketId in ticketPattern.findall(ticketList): 48 | tickets.setdefault(ticketId, []).append(commands[command.lower()]) 49 | 50 | for ticketId, commands in tickets.iteritems(): 51 | db = env.get_db_cnx() 52 | 53 | ticket = Ticket(env, int(ticketId), db) 54 | for command in commands: 55 | command(ticket) 56 | 57 | # determine sequence number... 58 | cnum = 0 59 | tm = TicketModule(env) 60 | for change in tm.grouped_changelog_entries(ticket, db): 61 | c_cnum = change.get('cnum', None) 62 | if c_cnum and int(c_cnum) > cnum: 63 | cnum = int(c_cnum) 64 | 65 | username = authorPattern.findall(author)[0] 66 | now = datetime.now(utc) 67 | message = "(On %s [changeset:%s %s]) %s" % (refname, rev, describe_tags, message) 68 | ticket['branch'] = refname 69 | ticket.save_changes(username, message, now, db, cnum+1) 70 | db.commit() 71 | 72 | tn = TicketNotifyEmail(env) 73 | tn.notify(ticket, newticket=0, modtime=now) 74 | 75 | -------------------------------------------------------------------------------- /server/post-receive.sample: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | nl=$'\n' 4 | input="" 5 | while read newref oldref refname ; do 6 | input="$input$newref $oldref $refname$nl" 7 | done 8 | 9 | echo -n "$input" | /srv/git/gc/server/post-receive-one 10 | echo -n "$input" | /srv/git/gc/server/post-receive-two 11 | 12 | -------------------------------------------------------------------------------- /server/update-allow-tags-branches: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # An example hook script to blocks unannotated tags from entering. 4 | # Called by git-receive-pack with arguments: refname sha1-old sha1-new 5 | # 6 | # Config 7 | # ------ 8 | # hooks.update-allow-tags-branches.unannotatedtag 9 | # This boolean sets whether unannotated tags will be allowed into the 10 | # repository. By default they won't be. 11 | # hooks.update-allow-tags-branches.deletetag 12 | # This boolean sets whether deleting tags will be allowed in the 13 | # repository. By default they won't be. 14 | # hooks.update-allow-tags-branches.deletebranch 15 | # This boolean sets whether deleting branches will be allowed in the 16 | # repository. By default they won't be. 17 | # hooks.update-allow-tags-branches.nakedtag 18 | # This boolean sets whether tags are allowed into the repo to commits 19 | # that are not pointed to by a branch. By default they won't be. 20 | # 21 | 22 | . $(dirname $0)/functions 23 | 24 | # --- Command line 25 | refname="$1" 26 | oldrev="$2" 27 | newrev="$3" 28 | 29 | # --- Config 30 | allowunannotatedtag=$(git config --bool hooks.update-allow-tags-branches.unannotatedtag) 31 | allowdeletebranch=$(git config --bool hooks.update-allow-tags-branches.deletebranch) 32 | allowdeletetag=$(git config --bool hooks.update-allow-tags-branches.deletetag) 33 | allownakedtag=$(git config --bool hooks.update-allow-tags-branches.nakedtag) 34 | 35 | # --- Check types 36 | # if $newrev is 0000...0000, it's a commit to delete a ref. 37 | if [ "$newrev" = "0000000000000000000000000000000000000000" ]; then 38 | newrev_type=delete 39 | else 40 | newrev_type=$(git-cat-file -t $newrev) 41 | fi 42 | 43 | case "$refname","$newrev_type" in 44 | refs/tags/*,commit) 45 | # un-annotated tag 46 | short_refname=${refname##refs/tags/} 47 | if [ "$allowunannotatedtag" != "true" ]; then 48 | display_error_message "Unannotated tags ($short_refname) are not allowed" 49 | exit 1 50 | fi 51 | contains=$(git branch --contains "$newrev" | wc -l) 52 | if [ $contains -eq 0 -a "$allownakedtag" != "true" ] ; then 53 | display_error_message "The tag $short_refname is not included in any branch" 54 | exit 1 55 | fi 56 | ;; 57 | refs/tags/*,delete) 58 | # delete tag 59 | if [ "$allowdeletetag" != "true" ]; then 60 | display_error_message "Deleting tags is not allowed" 61 | exit 1 62 | fi 63 | ;; 64 | refs/tags/*,tag) 65 | # annotated tag 66 | short_refname=${refname##refs/tags/} 67 | contains=$(git branch --contains "$newrev" | wc -l) 68 | if [ $contains -eq 0 -a "$allownakedtag" != "true" ] ; then 69 | display_error_message "The tag $short_refname is not included in any branch" 70 | exit 1 71 | fi 72 | ;; 73 | refs/heads/*,commit) 74 | # branch 75 | ;; 76 | refs/heads/*,delete) 77 | # delete branch 78 | if [ "$allowdeletebranch" != "true" ]; then 79 | echo "*** Deleting a branch is not allowed in this repository" >&2 80 | exit 1 81 | fi 82 | ;; 83 | refs/remotes/*,commit) 84 | # tracking branch 85 | ;; 86 | refs/remotes/*,delete) 87 | # delete tracking branch 88 | if [ "$allowdeletebranch" != "true" ]; then 89 | echo "*** Deleting a tracking branch is not allowed in this repository" >&2 90 | exit 1 91 | fi 92 | ;; 93 | *) 94 | # Anything else (is there anything else?) 95 | echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2 96 | exit 1 97 | ;; 98 | esac 99 | 100 | # --- Finished 101 | exit 0 102 | -------------------------------------------------------------------------------- /server/update-ensure-follows: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # When updating a branch, ensure it has the latest changes 4 | # from other branches, e.g. stable. 5 | # 6 | # While this forces merging sooner than devs may like, it 7 | # assures deployment and qa staff that the latest revisions 8 | # they are qa'ing will always have the last stable release 9 | # in it. 10 | # 11 | # Config 12 | # ------ 13 | # hooks.update-ensure-follows.branches 14 | # Space-separated list of branches that other branches must merge with 15 | # hooks.update-ensure-follows.excused 16 | # Space-separated list of branches that are excused from following (e.g. gitconfig) 17 | # 18 | 19 | . $(dirname $0)/functions 20 | 21 | # Command line 22 | refname="$1" 23 | oldrev="$2" 24 | newrev="$3" 25 | 26 | # Look up the config variable and exit if not set 27 | follows=$(git config hooks.update-ensure-follows.branches) 28 | if [[ $? -ne 0 ]] ; then 29 | exit 0 30 | fi 31 | 32 | # Branch deletions are okay 33 | if expr "$newrev" : '0*$' >/dev/null ; then 34 | exit 0 35 | fi 36 | 37 | # We only care about branches moving--ignore tags. 38 | case "$refname" in 39 | refs/heads/*) 40 | short_refname=${refname##refs/heads/} 41 | ;; 42 | *) 43 | exit 0 44 | ;; 45 | esac 46 | 47 | excused=" $(git config hooks.update-ensure-follows.excused) " 48 | if [[ $excused =~ " $short_refname " ]] ; then 49 | exit 0 50 | fi 51 | 52 | follows=($follows) 53 | count=${#follows[@]} 54 | for ((i = 0 ; i < count ; i++)) do 55 | follow="${follows[$i]}" 56 | git rev-parse --verify --quiet "$follow" 57 | if [ $? -eq 0 ] ; then 58 | missing_commits=$(git log ^$newrev $follow --pretty=oneline | wc -l) 59 | if [ $missing_commits -ne 0 ] ; then 60 | # If for some reason people are stupid and push with a --force flag, 61 | # we should warn them to update first in case one of their teammates 62 | # already merged for them 63 | if [ "0000000000000000000000000000000000000000" != "$oldrev" -a "$(git merge-base $oldrev $newrev)" != "$oldrev" ] ; then 64 | display_error_message "You need to update your local branch $short_refname" 65 | else 66 | display_error_message "You need to merge $follow into $short_refname" 67 | fi 68 | exit 1 69 | fi 70 | fi 71 | done 72 | 73 | exit 0 74 | 75 | -------------------------------------------------------------------------------- /server/update-ensure-ticket-reference: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | . $(dirname $0)/functions 4 | 5 | # Command line 6 | refname="$1" 7 | oldrev="$2" 8 | newrev="$3" 9 | 10 | # We only care about branches moving--ignore tags/etc. 11 | case "$refname" in 12 | refs/heads/*) 13 | short_refname=${refname##refs/heads/} 14 | ;; 15 | *) 16 | exit 0 17 | ;; 18 | esac 19 | 20 | excused=" $(git config hooks.update-ensure-ticket-reference.excused) " 21 | if [[ $excused =~ " $short_refname " ]] ; then 22 | exit 0 23 | fi 24 | 25 | if expr "$oldrev" : '0*$' >/dev/null ; then 26 | git rev-parse --not --branches | git rev-list --stdin --no-merges $newrev 27 | else 28 | git rev-parse --not --branches | git rev-list --stdin --no-merges $oldrev..$newrev 29 | fi | while read commit ; do 30 | # Have log dump the "subject line, new line, body" of each commit message for grepping 31 | git log -n 1 '--pretty=format:%s%n%b' "$commit" | grep -i '\(\(re\|refs\|qa\) #[0-9]\+\)\|\(no ticket\)' > /dev/null 32 | if [ $? -ne 0 ] ; then 33 | display_error_message "Commit $commit does not reference a ticket" 34 | exit 1 35 | fi 36 | done 37 | 38 | -------------------------------------------------------------------------------- /server/update-lock-check: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # 4 | # Checks $GIT_DIR/locked and $GIT_DIR/preserved for branches that cannot be 5 | # changed or deleted, respectively. 6 | # 7 | 8 | . $(dirname $0)/functions 9 | 10 | refname=$1 11 | oldrev=$2 12 | newrev=$3 13 | 14 | # We only care about branches 15 | case "$refname" in 16 | refs/heads/*) 17 | short_refname=${refname##refs/heads/} 18 | ;; 19 | *) 20 | exit 0 21 | ;; 22 | esac 23 | 24 | if test -f "$GIT_DIR/locked" ; then 25 | grep $short_refname "$GIT_DIR/locked" 26 | if [ $? -eq 0 ] ; then 27 | display_error_message "Branch $short_refname is locked" 28 | exit 1 29 | fi 30 | fi 31 | 32 | if test -f "$GIT_DIR/preserved" ; then 33 | grep $short_refname "$GIT_DIR/preserved" 34 | if [ $? -eq 0 ] ; then 35 | display_error_message "Branch $short_refname cannot be deleted" 36 | exit 1 37 | fi 38 | fi 39 | 40 | -------------------------------------------------------------------------------- /server/update-stable: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 4 | # This enforces stable moving in approved ways. 5 | # 6 | # Specifically: 7 | # 8 | # * stable must move by only 1 commit-per-push 9 | # * the stable commit must have 2 and only 2 parents 10 | # * The first parent must be the previous stable commit 11 | # * The second parent is the tip of the candidate branch being released 12 | # * the stable commit must have the same contents as the candidate tip 13 | # * Any merge conflicts should have been resolved in the candidate tip 14 | # by pulling stable into the candidate and having qa/tests done--pulling 15 | # candidate into stable should then apply cleanly 16 | # 17 | # For DAG aesthetics, we prefer stable only moving in the approved way, 18 | # which is via empty (no change) merge commits. The rationale is that 19 | # in the DAG we want a simple, one-commit move from each release to the 20 | # next. 21 | # 22 | # We started out with: 23 | # 24 | # * -- A stable 25 | # \ \ 26 | # \ * -- * -- B topic1 27 | # \ / 28 | # * -- * -- * topic2 29 | # 30 | # And then publishing stable was a matter of fast-forwarding 31 | # from A to B. 32 | # 33 | # In a complicated (non-rebased) DAG, this becomes hard to follow, 34 | # so want we want instead is: 35 | # 36 | # * -- A ----------- C stable 37 | # \ \ / 38 | # \ * -- * -- B topic1 39 | # \ / 40 | # * -- * -- * topic2 41 | # 42 | # Where commit C lists as it's first parent the prior stable 43 | # commit and as it's second parent the release candidate. No 44 | # other parents are allowed (e.g. no octopus merges here, which 45 | # would insinuate qa didn't happen on the merged result). 46 | # 47 | # Also, we want to enforce that C does not actually introduce 48 | # any diffs to the files between B and C--as they would be changes 49 | # that QA does not see. 50 | # 51 | 52 | . $(dirname $0)/functions 53 | 54 | refname="$1" 55 | oldrev="$2" 56 | newrev="$3" 57 | 58 | case "$refname" in 59 | refs/heads/*) 60 | short_refname=${refname##refs/heads/} 61 | ;; 62 | *) 63 | exit 0 64 | ;; 65 | esac 66 | 67 | set_change_type 68 | if [ "$change_type" == "delete" ] ; then 69 | exit 0 70 | fi 71 | 72 | # create/delete is okay 73 | if [ "$change_type" != "update" ] ; then 74 | exit 0 75 | fi 76 | 77 | if [ "$short_refname" == "stable" ] ; then 78 | # Stable enforcement 79 | 80 | # read backwards: 81 | # - all commits from old..new 82 | # - unless they were already pointed to by a branch 83 | # = all new commits on stable 84 | count=$(git rev-parse --not --branches | git rev-list --stdin $oldrev..$newrev | wc -l) 85 | if [ "$count" -ne "1" ] ; then 86 | display_error_message "Moving stable must entail a single commit" 87 | exit 1 88 | fi 89 | 90 | number_of_parents=$(git rev-list --no-walk --parents $newrev | sed 's/ /\n/g' | grep -v $newrev | wc -l) 91 | if [ "$number_of_parents" -ne "2" ] ; then 92 | display_error_message "Moving stable must entail a merge commit" 93 | exit 1 94 | fi 95 | 96 | first_parent=$(git rev-list --no-walk --parents $newrev | sed 's/ /\n/g' | grep -v $newrev | head --lines=1) 97 | if [ "$first_parent" != "$oldrev" ] ; then 98 | display_error_message "Moving stable must have the previous stable as the first parent" 99 | exit 1 100 | fi 101 | 102 | second_parent=$(git rev-list --no-walk --parents $newrev | sed 's/ /\n/g' | grep -v $newrev | tail --lines=1) 103 | changed_lines=$(git diff $second_parent..$newrev | wc -l) 104 | if [ "$changed_lines" -ne "0" ] ; then 105 | display_error_message "Moving stable must not result in any changes from $second_parent" 106 | exit 1 107 | fi 108 | fi 109 | 110 | -------------------------------------------------------------------------------- /server/update.sample: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | /srv/git/gc/server/update-one $1 $2 $3 && 4 | /srv/git/gc/server/update-two $1 $2 $3 5 | 6 | -------------------------------------------------------------------------------- /tests/.gitignore: -------------------------------------------------------------------------------- 1 | /trash directory 2 | -------------------------------------------------------------------------------- /tests/Makefile: -------------------------------------------------------------------------------- 1 | # Run tests 2 | # 3 | # Copyright (c) 2005 Junio C Hamano 4 | # 5 | 6 | #GIT_TEST_OPTS=--verbose --debug 7 | 8 | T = $(wildcard t[0-9][0-9][0-9][0-9]-*.sh) 9 | 10 | all: $(T) clean 11 | 12 | $(T): 13 | @echo "*** $@ ***"; GIT_CONFIG=.git/config /bin/bash $@ $(GIT_TEST_OPTS) 14 | 15 | clean: 16 | rm -fr 'trash directory' 17 | 18 | .PHONY: $(T) clean 19 | 20 | -------------------------------------------------------------------------------- /tests/t1000-commit-msg-trac.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | test_description='client commit-msg trac ticket enforcer' 4 | 5 | . ./test-lib.sh 6 | 7 | # setup the commit-msg hook 8 | install_client_hook 'commit-msg-trac' 'commit-msg' 9 | 10 | test_expect_success 'rejects with bad message' ' 11 | echo "$test_name" >file && 12 | git add file && 13 | ! git commit -m "first" 14 | ' 15 | 16 | test_expect_success 'rejects with re:' ' 17 | echo "$test_name" >file && 18 | git add file && 19 | ! git commit -m "first re: #3200" 20 | ' 21 | 22 | test_expect_success 'rejects with re no space' ' 23 | echo "$test_name" >file && 24 | git add file && 25 | ! git commit -m "first re#3200" 26 | ' 27 | 28 | test_expect_success 'accepts with re' ' 29 | echo "$test_name" >file && 30 | git add file && 31 | git commit -m "first re #3200" 32 | ' 33 | 34 | test_expect_success 'accepts with re on the second line' ' 35 | echo "$test_name" >file && 36 | git add file && 37 | echo "line one" >msg && 38 | echo "line two re #3200" >>msg && 39 | git commit -F msg 40 | ' 41 | 42 | test_expect_success 'accepts with RE' ' 43 | echo "$test_name" >file && 44 | git add file && 45 | git commit -m "first RE #3200" 46 | ' 47 | 48 | test_expect_success 'accepts with refs' ' 49 | echo "$test_name" >file && 50 | git add file && 51 | git commit -m "first refs #3200" 52 | ' 53 | 54 | test_expect_success 'accepts with qa' ' 55 | echo "$test_name" >file && 56 | git add file && 57 | git commit -m "first qa #3200" 58 | ' 59 | 60 | test_expect_success 'accepts with no ticket' ' 61 | echo "$test_name" >file && 62 | git add file && 63 | git commit -m "first no ticket" 64 | ' 65 | 66 | test_done 67 | 68 | -------------------------------------------------------------------------------- /tests/t1001-commit-msg-trac-merges.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | test_description='client commit-msg trac ticket enforcer for merges' 4 | 5 | . ./test-lib.sh 6 | 7 | # setup the commit-msg hook 8 | install_client_hook 'commit-msg-trac' 'commit-msg' 9 | 10 | test_expect_success 'accepts merge' ' 11 | echo "$test_name" >file && 12 | git add file && 13 | git commit -m "line one. re #3222." && 14 | git checkout -b topic1 && 15 | echo "$test_name topic1" >>file && 16 | git commit -a -m "line two. re #3222." && 17 | git checkout master && 18 | echo "$test_name" > file2 && 19 | git add file2 && 20 | git commit -m "file2. re #3222." && 21 | git merge topic1 && 22 | git log -n 1 HEAD | grep "Merge branch" 23 | ' 24 | 25 | test_expect_success 'accepts hand-editted merge' ' 26 | git checkout topic1 && 27 | echo "$test_name" >>file && 28 | git commit -a -m "line three. re #3222." && 29 | git checkout master && 30 | git merge --no-ff --no-commit topic1 && 31 | git commit -F .git/MERGE_MSG 32 | ' 33 | 34 | test_done 35 | 36 | -------------------------------------------------------------------------------- /tests/t2000-update-ensure-ticket-reference.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | test_description='server update trac ticket enforcer' 4 | 5 | . ./test-lib.sh 6 | 7 | test_expect_success 'setup' ' 8 | echo This is a test. >a && 9 | git add a && 10 | git commit -m "a" && 11 | git clone --bare -l ./ server.git && 12 | rm -fr server.git/hooks && 13 | git remote add origin ./server.git && 14 | git config --add branch.master.remote origin && 15 | git config --add branch.master.merge refs/heads/master 16 | ' 17 | 18 | # setup the update hook 19 | install_update_hook 'update-ensure-ticket-reference' 20 | 21 | test_expect_success 'reject with bad message' ' 22 | echo $test_name >a && 23 | git commit -a -m "$test_name" && 24 | head=$(git rev-parse HEAD) && 25 | ! git push 2>push.err && 26 | cat push.err | grep "Commit $head does not reference a ticket" 27 | ' 28 | 29 | # the last test has a dirty commit message, so ammend it with a good message 30 | test_expect_success 'accept with re' ' 31 | echo $test_name >a && 32 | git commit --amend -m "$test_name re #3222" && 33 | git push 34 | ' 35 | 36 | test_expect_success 'accept with re on second line' ' 37 | echo $test_name >a && 38 | echo "first subject line" >msg 39 | echo "second line re #322" >>msg 40 | git commit -a -F msg && 41 | git push 42 | ' 43 | 44 | test_expect_success 'reject with bad message in second of three' ' 45 | echo "$test_name first" >a && 46 | git commit -a -m "$test_name first re #3222" && 47 | 48 | # the bad one 49 | echo "$test_name second" >a && 50 | git commit -a -m "$test_name second" && 51 | head=$(git rev-parse HEAD) && 52 | echo "head=$head" && 53 | 54 | echo "$test_name third" >a && 55 | git commit -a -m "$test_name third re #3222" && 56 | 57 | ! git push 2>push.err && 58 | cat push.err | grep "Commit $head does not reference a ticket" 59 | ' 60 | 61 | test_expect_success 'accept with re in all of three' ' 62 | git reset --hard HEAD^^^ && 63 | echo "$test_name first" >a && 64 | git commit -a -m "$test_name first re #3222" && 65 | 66 | # the bad one 67 | echo "$test_name second" >a && 68 | git commit -a -m "$test_name second re #3222" && 69 | head=$(git rev-parse HEAD) && 70 | 71 | echo "$test_name third" >a && 72 | git commit -a -m "$test_name third re #3222" && 73 | 74 | git push 75 | ' 76 | 77 | test_done 78 | 79 | -------------------------------------------------------------------------------- /tests/t2001-update-ensure-ticket-reference-merges.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | test_description='server update trac ticket enforcer via shim' 4 | 5 | . ./test-lib.sh 6 | 7 | test_expect_success 'setup' ' 8 | echo "setup" >a && 9 | git add a && 10 | git commit -m "setup" && 11 | git clone ./. server && 12 | rm -fr server/.git/hooks && 13 | git remote add origin ./server && 14 | git config --add branch.master.remote origin && 15 | git config --add branch.master.merge refs/heads/master 16 | ' 17 | 18 | # setup the hook 19 | install_update_hook 'update-ensure-ticket-reference' 20 | 21 | test_expect_success 'accept merge with merge message' ' 22 | git checkout -b topic1 master && 23 | echo "$test_name" >a1 && 24 | git add a1 && 25 | git commit -m "$test_name topic1 re #1" && 26 | git push origin topic1 && 27 | 28 | git checkout -b topic2 master && 29 | echo "$test_name" >a2 && 30 | git add a2 && 31 | git commit -m "$test_name topic2 re #2" && 32 | git push origin topic2 && 33 | 34 | git checkout topic1 && 35 | echo "$test_name" >>a1 && 36 | git commit -a -m "$test_name topic1 re #1 again" && 37 | git merge topic2 && 38 | git push 39 | ' 40 | 41 | test_done 42 | 43 | -------------------------------------------------------------------------------- /tests/t2002-update-ensure-ticket-reference-branches.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | test_description='server update trac ticket enforcer via shim' 4 | 5 | . ./test-lib.sh 6 | 7 | test_expect_success 'setup' ' 8 | echo "setup" >a && 9 | git add a && 10 | git commit -m "setup" && 11 | git clone -l . --bare server.git && 12 | rm -fr server.git/hooks && 13 | git remote add origin ./server.git && 14 | git config --add branch.master.remote origin && 15 | git config --add branch.master.merge refs/heads/master 16 | ' 17 | 18 | # setup the hook 19 | install_update_hook 'update-ensure-ticket-reference' 20 | 21 | test_expect_success 'reject new branch with bad message' ' 22 | git checkout -b topic1 master && 23 | echo $test_name >a && 24 | git commit -a -m "$test_name" && 25 | head=$(git rev-parse HEAD) 26 | ! git push origin topic1 >push.out 2>push.err && 27 | cat push.err | grep "Commit $head does not reference a ticket" 28 | ' 29 | 30 | # the last test has a dirty commit message, so ammend it with a good message 31 | test_expect_success 'accept new branch with re' ' 32 | git checkout -b topic2 master && 33 | echo $test_name >a && 34 | git commit --amend -m "$test_name re #3222" && 35 | git push origin topic2 36 | ' 37 | 38 | test_expect_success 'reject new branch with bad message in second of three' ' 39 | git checkout -b topic3 master && 40 | echo "$test_name first" >a && 41 | git commit -a -m "$test_name first re #3222" && 42 | 43 | # the bad one 44 | echo "$test_name second" >a && 45 | git commit -a -m "$test_name second" && 46 | head=$(git rev-parse HEAD) && 47 | 48 | echo "$test_name third" >a && 49 | git commit -a -m "$test_name third re #3222" && 50 | 51 | ! git push origin topic3 >push.out 2>push.err && 52 | cat push.err | grep "Commit $head does not reference a ticket" 53 | ' 54 | 55 | test_expect_success 'accept new branch with re in all of three' ' 56 | git checkout -b topic4 master && 57 | echo "$test_name first" >a && 58 | git commit -a -m "$test_name first re #3222" && 59 | 60 | # the bad one 61 | echo "$test_name second" >a && 62 | git commit -a -m "$test_name second re #3222" && 63 | head=$(git rev-parse HEAD) && 64 | 65 | echo "$test_name third" >a && 66 | git commit -a -m "$test_name third re #3222" && 67 | 68 | git push origin topic4 69 | ' 70 | 71 | test_expect_success 'accept branch that has been excused' ' 72 | git checkout -b topic5 master && 73 | echo "$test_name first" >a && 74 | git commit -a -m "$test_name first with no re" && 75 | 76 | ! git push origin topic5 77 | 78 | cd server.git 79 | git config hooks.update-ensure-ticket-reference.excused topic5 80 | cd .. 81 | 82 | git push origin topic5 83 | ' 84 | 85 | test_done 86 | 87 | -------------------------------------------------------------------------------- /tests/t2100-update-stable.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | test_description='server update stable enforcer' 4 | 5 | . ./test-lib.sh 6 | 7 | test_expect_success 'setup' ' 8 | echo setup >a && 9 | git add a && 10 | git commit -m "a" && 11 | git clone -l . --bare server.git && 12 | git remote add origin ./server.git && 13 | rm -fr server.git/hooks 14 | ' 15 | 16 | # setup the update hook 17 | install_update_hook 'update-stable' 18 | 19 | test_expect_success 'initial stable commit works' ' 20 | # do one stable-less commit 21 | echo $test_name >a && 22 | git commit -a -m "$test_name" && 23 | git push origin master && 24 | 25 | git checkout -b stable && 26 | git push origin stable && 27 | git config --add branch.stable.remote origin && 28 | git config --add branch.stable.merge refs/heads/stable 29 | ' 30 | 31 | test_expect_success 'reject commit directly to stable' ' 32 | echo $test_name >a && 33 | git commit -a -m "$test_name going onto stable" && 34 | head=$(git rev-parse HEAD) && 35 | ! git push 2>push.err && 36 | cat push.err | grep "Moving stable must entail a merge commit" && 37 | git reset --hard HEAD^ 38 | ' 39 | 40 | test_expect_success 'reject fast-forward to candidate branch' ' 41 | # make one topic branch 42 | git checkout -b topic1 stable && 43 | echo $test_name >topic1 && 44 | git add topic1 && 45 | git commit -m "$test_name topic1" && 46 | git push origin topic1 && 47 | 48 | git checkout stable && 49 | git merge topic1 >merge.out && 50 | cat merge.out | grep "Fast-forward" && 51 | ! git push 2>push.err && 52 | cat push.err | grep "Moving stable must entail a single commit" && 53 | git reset --hard ORIG_HEAD 54 | ' 55 | 56 | test_expect_success 'reject merge with wrong first-parent' ' 57 | # make one topic branch 58 | git checkout -b topic2 stable && 59 | echo $test_name >topic2 && 60 | git add topic2 && 61 | git commit -m "$test_name topic2" && 62 | git push origin topic2 && 63 | 64 | # move ahead stable by topic3 65 | git checkout -b topic3 stable && 66 | echo $test_name >topic3 && 67 | git add topic3 && 68 | git commit -m "$test_name topic3" && 69 | git push origin topic3 && 70 | git checkout stable && 71 | git merge --no-ff topic3 && 72 | git push && 73 | 74 | # back to topic2, merge in stable, and try to push it out as the new stable 75 | git checkout topic2 && 76 | git merge stable && 77 | ! git push origin topic2:refs/heads/stable 2>push.err && 78 | cat push.err | grep "Moving stable must have the previous stable as the first parent" && 79 | 80 | # Go ahead and push topic2 itself 81 | git push && 82 | 83 | # but merging into stable should still work fine 84 | git checkout stable && 85 | git merge --no-ff topic2 && 86 | git push 87 | ' 88 | 89 | test_expect_success 'reject merge with changes' ' 90 | # make one topic branch 91 | git checkout -b topic4 stable && 92 | echo $test_name >topic4 && 93 | git add topic4 && 94 | git commit -m "$test_name topic4" && 95 | git push origin topic4 && 96 | 97 | # move ahead stable by topic5 98 | git checkout -b topic5 stable && 99 | echo $test_name >topic5 && 100 | git add topic5 && 101 | git commit -m "$test_name topic5" && 102 | git push origin topic5 && 103 | git checkout stable && 104 | git merge --no-ff topic5 && 105 | git push && 106 | 107 | # try merging topic4 into stable, which will get a merge commit, but 108 | # it should have changes involved and so get rejected 109 | git checkout stable && 110 | topic4_hash=$(git rev-parse topic4) && 111 | git merge topic4 && 112 | ! git push 2>push.err && 113 | cat push.err | grep "Moving stable must not result in any changes from $topic4_hash" 114 | ' 115 | 116 | test_done 117 | 118 | -------------------------------------------------------------------------------- /tests/t2200-1.txt: -------------------------------------------------------------------------------- 1 | From: author@example.com 2 | To: commits@list.com 3 | Subject: [cbas] master branch updated. $new_commit_hash 4 | X-Git-Refname: refs/heads/master 5 | X-Git-Reftype: branch 6 | X-Git-Oldrev: $old_commit_hash 7 | X-Git-Newrev: $new_commit_hash 8 | 9 | The branch, master has been updated 10 | via $new_commit_abbrev simple commit 11 | from $old_commit_abbrev setup 12 | 13 | Those revisions listed above that are new to this repository have 14 | not appeared on any other notification email; so we list those 15 | revisions in full, below. 16 | 17 | - Log ----------------------------------------------------------------- 18 | 19 | commit $new_commit_hash 20 | Author: A U Thor 21 | Date: $new_commit_date 22 | 23 | simple commit 24 | 25 | $new_commit_hash 26 | diff --git a/a b/a 27 | index 146f275..b38f23c 100644 28 | --- a/a 29 | +++ b/a 30 | @@ -1 +1 @@ 31 | -setup 32 | +simple commit 33 | 34 | ----------------------------------------------------------------------- 35 | 36 | Summary of changes: 37 | a | 2 +- 38 | 1 files changed, 1 insertions(+), 1 deletions(-) 39 | -------------------------------------------------------------------------------- /tests/t2200-2.txt: -------------------------------------------------------------------------------- 1 | From: author@example.com 2 | To: commits@list.com 3 | Subject: [cbas] master branch updated. r/1000 4 | X-Git-Refname: refs/heads/master 5 | X-Git-Reftype: branch 6 | X-Git-Oldrev: $old_commit_hash 7 | X-Git-Newrev: $new_commit_hash 8 | 9 | The branch, master has been updated 10 | via $new_commit_abbrev simple commit with commitnumber 11 | from $old_commit_abbrev simple commit 12 | 13 | Those revisions listed above that are new to this repository have 14 | not appeared on any other notification email; so we list those 15 | revisions in full, below. 16 | 17 | - Log ----------------------------------------------------------------- 18 | 19 | commit $new_commit_hash 20 | Author: A U Thor 21 | Date: $new_commit_date 22 | 23 | simple commit with commitnumber 24 | 25 | $new_commit_hash 26 | diff --git a/a b/a 27 | index b38f23c..bd6aaf6 100644 28 | --- a/a 29 | +++ b/a 30 | @@ -1 +1 @@ 31 | -simple commit 32 | +simple commit with commitnumber 33 | 34 | ----------------------------------------------------------------------- 35 | 36 | Summary of changes: 37 | a | 2 +- 38 | 1 files changed, 1 insertions(+), 1 deletions(-) 39 | -------------------------------------------------------------------------------- /tests/t2200-post-receive-email.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | test_description='server post-receive email notification' 4 | 5 | . ./test-lib.sh 6 | 7 | export USER_EMAIL=author@example.com 8 | 9 | test_expect_success 'setup' ' 10 | echo "setup" >a && 11 | git add a && 12 | git commit -m "setup" && 13 | git clone -l . --bare server.git && 14 | rm -fr server.git/hooks && 15 | git remote add origin ./server.git && 16 | git config branch.master.remote origin && 17 | git config branch.master.merge refs/heads/master && 18 | GIT_DIR=./server.git git config hooks.post-receive-email.mailinglist commits@list.com && 19 | GIT_DIR=./server.git git config hooks.post-receive-email.debug true && 20 | echo cbas >./server.git/description 21 | ' 22 | 23 | install_post_receive_hook 'post-receive-email' 24 | 25 | test_expect_success 'simple commit' ' 26 | old_commit_hash=$(git rev-parse HEAD) && 27 | old_commit_abbrev=$(git rev-parse --short HEAD) && 28 | 29 | echo $test_name >a && 30 | git commit -a -m "$test_name" && 31 | git push && 32 | new_commit_hash=$(git rev-parse HEAD) && 33 | new_commit_date=$(git log -n 1 --pretty=format:%cd HEAD) && 34 | new_commit_abbrev=$(git rev-parse --short HEAD) && 35 | 36 | interpolate ../t2200-1.txt 1.txt old_commit_hash old_commit_abbrev new_commit_hash new_commit_date new_commit_abbrev && 37 | test_cmp 1.txt server.git/refs.heads.master.out 38 | ' 39 | 40 | test_expect_success 'simple commit with commitnumber' ' 41 | old_commit_hash=$(git rev-parse HEAD) && 42 | old_commit_abbrev=$(git rev-parse --short HEAD) && 43 | 44 | echo $test_name >a && 45 | git commit -a -m "$test_name" && 46 | git tag r/1000 && 47 | git push --tags && 48 | git push && 49 | new_commit_hash=$(git rev-parse HEAD) && 50 | new_commit_date=$(git log -n 1 --pretty=format:%cd HEAD) && 51 | new_commit_abbrev=$(git rev-parse --short HEAD) && 52 | 53 | interpolate ../t2200-2.txt 2.txt old_commit_hash old_commit_abbrev new_commit_hash new_commit_date new_commit_abbrev && 54 | test_cmp 2.txt server.git/refs.heads.master.out 55 | ' 56 | 57 | test_done 58 | 59 | -------------------------------------------------------------------------------- /tests/t2201-1.txt: -------------------------------------------------------------------------------- 1 | From: author@example.com 2 | To: commits@list.com 3 | Subject: [cbas] 1.0 annotated tag created. 1.0 4 | X-Git-Refname: refs/tags/1.0 5 | X-Git-Reftype: annotated tag 6 | X-Git-Oldrev: 0000000000000000000000000000000000000000 7 | X-Git-Newrev: $tag_hash 8 | 9 | The annotated tag, 1.0 has been created 10 | at $tag_hash (tag) 11 | tagging $new_commit_hash (commit) 12 | tagged by C O Mitter 13 | on $tag_date 14 | 15 | - Log ----------------------------------------------------------------- 16 | 1.0 17 | 18 | A U Thor (1): 19 | setup 20 | 21 | ----------------------------------------------------------------------- 22 | -------------------------------------------------------------------------------- /tests/t2201-2.txt: -------------------------------------------------------------------------------- 1 | From: author@example.com 2 | To: commits@list.com 3 | Subject: [cbas] master branch updated. 1.0-2-g$new_commit_abbrev 4 | X-Git-Refname: refs/heads/master 5 | X-Git-Reftype: branch 6 | X-Git-Oldrev: $old_commit_hash 7 | X-Git-Newrev: $new_commit_hash 8 | 9 | The branch, master has been updated 10 | via $new_commit_abbrev commit on annotated tagged branch 2 11 | via $prior_commit_abbrev commit on annotated tagged branch 12 | from $old_commit_abbrev setup 13 | 14 | Those revisions listed above that are new to this repository have 15 | not appeared on any other notification email; so we list those 16 | revisions in full, below. 17 | 18 | - Log ----------------------------------------------------------------- 19 | 20 | commit $prior_commit_hash 21 | Author: A U Thor 22 | Date: $prior_commit_date 23 | 24 | commit on annotated tagged branch 25 | 26 | $prior_commit_hash 27 | diff --git a/a b/a 28 | index 146f275..815c4fa 100644 29 | --- a/a 30 | +++ b/a 31 | @@ -1 +1 @@ 32 | -setup 33 | +commit on annotated tagged branch 34 | 35 | ----------------------------------------------------------------------- 36 | 37 | commit $new_commit_hash 38 | Author: A U Thor 39 | Date: $new_commit_date 40 | 41 | commit on annotated tagged branch 2 42 | 43 | $new_commit_hash 44 | diff --git a/a b/a 45 | index 815c4fa..7d07572 100644 46 | --- a/a 47 | +++ b/a 48 | @@ -1 +1 @@ 49 | -commit on annotated tagged branch 50 | +commit on annotated tagged branch 2 51 | 52 | ----------------------------------------------------------------------- 53 | 54 | Summary of changes: 55 | a | 2 +- 56 | 1 files changed, 1 insertions(+), 1 deletions(-) 57 | -------------------------------------------------------------------------------- /tests/t2201-3.txt: -------------------------------------------------------------------------------- 1 | From: author@example.com 2 | To: commits@list.com 3 | Subject: [cbas] 2.0 annotated tag created. 2.0 4 | X-Git-Refname: refs/tags/2.0 5 | X-Git-Reftype: annotated tag 6 | X-Git-Oldrev: 0000000000000000000000000000000000000000 7 | X-Git-Newrev: $tag_hash 8 | 9 | The annotated tag, 2.0 has been created 10 | at $tag_hash (tag) 11 | tagging $new_commit_hash (commit) 12 | replaces 1.0 13 | tagged by C O Mitter 14 | on $tag_date 15 | 16 | - Log ----------------------------------------------------------------- 17 | 2.0 18 | 19 | A U Thor (2): 20 | commit on annotated tagged branch 21 | commit on annotated tagged branch 2 22 | 23 | ----------------------------------------------------------------------- 24 | -------------------------------------------------------------------------------- /tests/t2201-4.txt: -------------------------------------------------------------------------------- 1 | From: author@example.com 2 | To: commits@list.com 3 | Subject: [cbas] 2.1 tag created. 2.1 4 | X-Git-Refname: refs/tags/2.1 5 | X-Git-Reftype: tag 6 | X-Git-Oldrev: 0000000000000000000000000000000000000000 7 | X-Git-Newrev: $new_commit_hash 8 | 9 | The tag, 2.1 has been created 10 | at $new_commit_hash (commit) 11 | 12 | - Log ----------------------------------------------------------------- 13 | commit $new_commit_hash 14 | Author: A U Thor 15 | Date: $new_commit_date 16 | 17 | create lightweight tag 18 | ----------------------------------------------------------------------- 19 | -------------------------------------------------------------------------------- /tests/t2201-5.txt: -------------------------------------------------------------------------------- 1 | From: author@example.com 2 | To: commits@list.com 3 | Subject: [cbas] 2.1 tag updated. 2.1 4 | X-Git-Refname: refs/tags/2.1 5 | X-Git-Reftype: tag 6 | X-Git-Oldrev: $old_commit_hash 7 | X-Git-Newrev: $new_commit_hash 8 | 9 | The tag, 2.1 has been updated 10 | to $new_commit_hash (commit) 11 | from $old_commit_hash (commit) 12 | 13 | - Log ----------------------------------------------------------------- 14 | commit $new_commit_hash 15 | Author: A U Thor 16 | Date: $new_commit_date 17 | 18 | force update lightweight tag 19 | ----------------------------------------------------------------------- 20 | -------------------------------------------------------------------------------- /tests/t2201-6.txt: -------------------------------------------------------------------------------- 1 | From: author@example.com 2 | To: commits@list.com 3 | Subject: [cbas] 2.1 tag deleted. $old_commit_describe 4 | X-Git-Refname: refs/tags/2.1 5 | X-Git-Reftype: tag 6 | X-Git-Oldrev: $old_commit_hash 7 | X-Git-Newrev: 0000000000000000000000000000000000000000 8 | 9 | The tag, 2.1 has been deleted 10 | was $old_commit_hash (commit) 11 | 12 | ----------------------------------------------------------------------- 13 | $old_commit_hash force update lightweight tag 14 | ----------------------------------------------------------------------- 15 | -------------------------------------------------------------------------------- /tests/t2201-7.txt: -------------------------------------------------------------------------------- 1 | From: author@example.com 2 | To: commits@list.com 3 | Subject: [cbas] 2.0 annotated tag updated. 2.0 4 | X-Git-Refname: refs/tags/2.0 5 | X-Git-Reftype: annotated tag 6 | X-Git-Oldrev: $old_tag_hash 7 | X-Git-Newrev: $new_tag_hash 8 | 9 | The annotated tag, 2.0 has been updated 10 | to $new_tag_hash (tag) 11 | from $old_tag_hash (which is now obsolete) 12 | tagging $new_commit_hash (commit) 13 | replaces 1.0 14 | tagged by C O Mitter 15 | on $tag_date 16 | 17 | - Log ----------------------------------------------------------------- 18 | 2.0 19 | 20 | A U Thor (3): 21 | commit on annotated tagged branch 22 | commit on annotated tagged branch 2 23 | force update annotated tag 24 | 25 | ----------------------------------------------------------------------- 26 | -------------------------------------------------------------------------------- /tests/t2201-8.txt: -------------------------------------------------------------------------------- 1 | From: author@example.com 2 | To: commits@list.com 3 | Subject: [cbas] 2.0 annotated tag deleted. $new_commit_describe 4 | X-Git-Refname: refs/tags/2.0 5 | X-Git-Reftype: annotated tag 6 | X-Git-Oldrev: $old_tag_hash 7 | X-Git-Newrev: 0000000000000000000000000000000000000000 8 | 9 | The annotated tag, 2.0 has been deleted 10 | was $old_tag_hash (tag) 11 | 12 | ----------------------------------------------------------------------- 13 | tag 2.0 14 | 15 | 2.0 16 | $new_commit_hash force update annotated tag 17 | ----------------------------------------------------------------------- 18 | -------------------------------------------------------------------------------- /tests/t2201-post-receive-email-tags.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | test_description='server post-receive email notification' 4 | 5 | . ./test-lib.sh 6 | 7 | export USER_EMAIL=author@example.com 8 | 9 | test_expect_success 'setup' ' 10 | echo "setup" >a && 11 | git add a && 12 | git commit -m "setup" && 13 | git clone -l . --bare server.git && 14 | rm -fr server.git/hooks && 15 | git remote add origin ./server.git && 16 | git config branch.master.remote origin && 17 | git config branch.master.merge refs/heads/master && 18 | GIT_DIR=./server.git git config hooks.post-receive-email.mailinglist commits@list.com && 19 | GIT_DIR=./server.git git config hooks.post-receive-email.debug true && 20 | echo cbas >./server.git/description 21 | ' 22 | 23 | install_post_receive_hook 'post-receive-email' 24 | 25 | test_expect_success 'create annotated tag' ' 26 | git tag -a -m 1.0 1.0 && 27 | git push --tags && 28 | new_commit_hash=$(git rev-parse HEAD) && 29 | tag_hash=$(git rev-parse 1.0) && 30 | eval $(git for-each-ref --shell "--format=tag_date=%(taggerdate)" refs/tags/1.0) && 31 | 32 | interpolate ../t2201-1.txt 1.txt new_commit_hash tag_hash tag_date && 33 | test_cmp 1.txt server.git/refs.tags.1.0.out 34 | ' 35 | 36 | test_expect_success 'commit on annotated tagged branch' ' 37 | old_commit_hash=$(git rev-parse HEAD) && 38 | old_commit_abbrev=$(git rev-parse --short HEAD) && 39 | 40 | echo "$test_name" >a && 41 | git commit -a -m "$test_name" && 42 | prior_commit_hash=$(git rev-parse HEAD) && 43 | prior_commit_date=$(git log -n 1 --pretty=format:%cd HEAD) && 44 | prior_commit_abbrev=$(git rev-parse --short HEAD) && 45 | 46 | echo "$test_name 2" >a && 47 | git commit -a -m "$test_name 2" && 48 | new_commit_hash=$(git rev-parse HEAD) && 49 | new_commit_date=$(git log -n 1 --pretty=format:%cd HEAD) && 50 | new_commit_abbrev=$(git rev-parse --short HEAD) && 51 | 52 | git push && 53 | new_commit_abbrev=$(git rev-list -n 1 --pretty=format:%h HEAD | grep -v commit) && 54 | interpolate ../t2201-2.txt 2.txt old_commit_hash new_commit_hash new_commit_date new_commit_abbrev prior_commit_hash prior_commit_date old_commit_abbrev prior_commit_abbrev new_commit_abbrev && 55 | test_cmp 2.txt server.git/refs.heads.master.out 56 | ' 57 | 58 | test_expect_success 're-annotated tag branch' ' 59 | git tag -a -m 2.0 2.0 && 60 | git push --tags && 61 | new_commit_hash=$(git rev-parse HEAD) && 62 | tag_hash=$(git rev-parse 2.0) && 63 | eval $(git for-each-ref --shell "--format=tag_date=%(taggerdate)" refs/tags/2.0) && 64 | 65 | interpolate ../t2201-3.txt 3.txt new_commit_hash tag_hash tag_date && 66 | test_cmp 3.txt server.git/refs.tags.2.0.out 67 | ' 68 | 69 | test_expect_success 'force update annotated tag' ' 70 | old_tag_hash=$(git rev-parse 2.0) && 71 | 72 | echo "$test_name" >a && 73 | git commit -a -m "$test_name" && 74 | new_commit_hash=$(git rev-parse HEAD) && 75 | new_commit_date=$(git log -n 1 --pretty=format:%cd HEAD) && 76 | 77 | git tag -f -a -m 2.0 2.0 && 78 | git push --tags && 79 | new_tag_hash=$(git rev-parse 2.0) && 80 | eval $(git for-each-ref --shell "--format=tag_date=%(taggerdate)" refs/tags/2.0) && 81 | 82 | interpolate ../t2201-7.txt 7.txt old_tag_hash new_commit_hash new_tag_hash tag_date && 83 | test_cmp 7.txt server.git/refs.tags.2.0.out 84 | ' 85 | 86 | test_expect_success 'delete annotated tag' ' 87 | old_tag_hash=$(git rev-parse 2.0) && 88 | eval $(git for-each-ref --shell "--format=old_tag_date=%(taggerdate)" refs/tags/2.0) && 89 | 90 | git tag -d 2.0 && 91 | git push origin :refs/tags/2.0 && 92 | 93 | new_commit_describe=$(git describe HEAD) && 94 | new_commit_hash=$(git rev-parse HEAD) && 95 | 96 | interpolate ../t2201-8.txt 8.txt old_tag_hash old_tag_date new_commit_describe new_commit_hash && 97 | test_cmp 8.txt server.git/refs.tags.2.0.out 98 | ' 99 | 100 | test_expect_success 'create lightweight tag' ' 101 | echo "$test_name" >a && 102 | git commit -a -m "$test_name" && 103 | git push && 104 | 105 | git tag 2.1 && 106 | git push --tags && 107 | new_commit_hash=$(git rev-parse HEAD) && 108 | new_commit_describe=$(git describe HEAD) && 109 | new_commit_date=$(git rev-list --no-walk --pretty=format:%ad HEAD | tail -n 1) && 110 | 111 | interpolate ../t2201-4.txt 4.txt new_commit_hash new_commit_describe new_commit_date && 112 | test_cmp 4.txt server.git/refs.tags.2.1.out 113 | ' 114 | 115 | test_expect_success 'force update lightweight tag' ' 116 | old_commit_hash=$(git rev-parse HEAD) && 117 | echo "$test_name" >a && 118 | git commit -a -m "$test_name" && 119 | git push && 120 | 121 | git tag -f 2.1 && 122 | git push --tags && 123 | new_commit_hash=$(git rev-parse HEAD) && 124 | new_commit_describe=$(git describe HEAD) && 125 | new_commit_date=$(git rev-list --no-walk --pretty=format:%ad HEAD | tail -n 1) && 126 | 127 | interpolate ../t2201-5.txt 5.txt new_commit_hash new_commit_describe new_commit_date old_commit_hash && 128 | test_cmp 5.txt server.git/refs.tags.2.1.out 129 | ' 130 | 131 | test_expect_success 'delete lightweight tag' ' 132 | old_commit_hash=$(git rev-parse HEAD) && 133 | old_commit_describe=$(git describe HEAD) && 134 | git tag -d 2.1 && 135 | git push origin :refs/tags/2.1 && 136 | 137 | interpolate ../t2201-6.txt 6.txt old_commit_hash old_commit_describe && 138 | test_cmp 6.txt server.git/refs.tags.2.1.out 139 | ' 140 | 141 | test_done 142 | 143 | -------------------------------------------------------------------------------- /tests/t2202-1.txt: -------------------------------------------------------------------------------- 1 | From: author@example.com 2 | To: commits@list.com 3 | Subject: [cbas] topic branch created. $new_commit_hash 4 | X-Git-Refname: refs/heads/topic 5 | X-Git-Reftype: branch 6 | X-Git-Oldrev: 0000000000000000000000000000000000000000 7 | X-Git-Newrev: $new_commit_hash 8 | 9 | The branch, topic has been created 10 | at $new_commit_abbrev create branch on topic 2 11 | 12 | - Log ----------------------------------------------------------------- 13 | 14 | commit $prior_commit_hash 15 | Author: A U Thor 16 | Date: $prior_commit_date 17 | 18 | create branch on topic 19 | 20 | $prior_commit_hash 21 | diff --git a/a b/a 22 | index 146f275..55da47f 100644 23 | --- a/a 24 | +++ b/a 25 | @@ -1 +1 @@ 26 | -setup 27 | +create branch 28 | 29 | ----------------------------------------------------------------------- 30 | 31 | commit $new_commit_hash 32 | Author: A U Thor 33 | Date: $new_commit_date 34 | 35 | create branch on topic 2 36 | 37 | $new_commit_hash 38 | diff --git a/a b/a 39 | index 55da47f..8516a40 100644 40 | --- a/a 41 | +++ b/a 42 | @@ -1 +1 @@ 43 | -create branch 44 | +create branch 2 45 | 46 | ----------------------------------------------------------------------- 47 | 48 | Summary of changes: 49 | a | 2 +- 50 | 1 files changed, 1 insertions(+), 1 deletions(-) 51 | -------------------------------------------------------------------------------- /tests/t2202-2.txt: -------------------------------------------------------------------------------- 1 | From: author@example.com 2 | To: commits@list.com 3 | Subject: [cbas] topic branch deleted. $old_commit_hash 4 | X-Git-Refname: refs/heads/topic 5 | X-Git-Reftype: branch 6 | X-Git-Oldrev: $old_commit_hash 7 | X-Git-Newrev: 0000000000000000000000000000000000000000 8 | 9 | The branch, topic has been deleted 10 | was $old_commit_hash 11 | 12 | ----------------------------------------------------------------------- 13 | $old_commit_hash rewind and continue branch on topic 14 | ----------------------------------------------------------------------- 15 | -------------------------------------------------------------------------------- /tests/t2202-3.txt: -------------------------------------------------------------------------------- 1 | From: author@example.com 2 | To: commits@list.com 3 | Subject: [cbas] topic2 branch created. $existing_commit_hash 4 | X-Git-Refname: refs/heads/topic2 5 | X-Git-Reftype: branch 6 | X-Git-Oldrev: 0000000000000000000000000000000000000000 7 | X-Git-Newrev: $existing_commit_hash 8 | 9 | The branch, topic2 has been created 10 | at $existing_commit_abbrev create branch on topic 2 11 | 12 | - Log ----------------------------------------------------------------- 13 | -------------------------------------------------------------------------------- /tests/t2202-4.txt: -------------------------------------------------------------------------------- 1 | From: author@example.com 2 | To: commits@list.com 3 | Subject: [cbas] topic branch updated. $existing_commit_hash 4 | X-Git-Refname: refs/heads/topic 5 | X-Git-Reftype: branch 6 | X-Git-Oldrev: $old_commit_hash 7 | X-Git-Newrev: $existing_commit_hash 8 | 9 | The branch, topic has been updated 10 | via $existing_commit_abbrev update branch with existing commits does not replay them on topic 11 | from $old_commit_abbrev create branch on topic 2 12 | 13 | Those revisions listed above that are new to this repository have 14 | not appeared on any other notification email; so we list those 15 | revisions in full, below. 16 | 17 | - Log ----------------------------------------------------------------- 18 | 19 | Summary of changes: 20 | a | 2 +- 21 | 1 files changed, 1 insertions(+), 1 deletions(-) 22 | -------------------------------------------------------------------------------- /tests/t2202-5.txt: -------------------------------------------------------------------------------- 1 | From: author@example.com 2 | To: commits@list.com 3 | Subject: [cbas] topic branch updated. $new_commit_hash 4 | X-Git-Refname: refs/heads/topic 5 | X-Git-Reftype: branch 6 | X-Git-Oldrev: $old_commit_hash 7 | X-Git-Newrev: $new_commit_hash 8 | 9 | The branch, topic has been updated 10 | discards $old_commit_abbrev update branch with existing commits does not replay them on topic 11 | 12 | This update discarded existing revisions and left the branch pointing at 13 | a previous point in the repository history. 14 | 15 | * -- * -- N ($new_commit_hash) 16 | \ 17 | O -- O -- O ($old_commit_hash) 18 | 19 | The removed revisions are not necessarilly gone - if another reference 20 | still refers to them they will stay in the repository. 21 | 22 | No new revisions were added by this update. 23 | 24 | Summary of changes: 25 | a | 2 +- 26 | 1 files changed, 1 insertions(+), 1 deletions(-) 27 | -------------------------------------------------------------------------------- /tests/t2202-6.txt: -------------------------------------------------------------------------------- 1 | From: author@example.com 2 | To: commits@list.com 3 | Subject: [cbas] topic branch updated. $new_commit_hash 4 | X-Git-Refname: refs/heads/topic 5 | X-Git-Reftype: branch 6 | X-Git-Oldrev: $old_commit_hash 7 | X-Git-Newrev: $new_commit_hash 8 | 9 | The branch, topic has been updated 10 | discards $old_commit_abbrev create branch on topic 2 11 | via $new_commit_abbrev rewind and continue branch on topic 12 | 13 | This update added new revisions after undoing existing revisions. That is 14 | to say, the old revision is not a strict subset of the new revision. This 15 | situation occurs when you --force push a change and generate a repository 16 | containing something like this: 17 | 18 | * -- * -- B -- O -- O -- O ($old_commit_hash) 19 | \ 20 | N -- N -- N ($new_commit_hash) 21 | 22 | When this happens we assume that you've already had alert emails for all 23 | of the O revisions, and so we here report only the revisions in the N 24 | branch from the common base, B. 25 | 26 | Those revisions listed above that are new to this repository have 27 | not appeared on any other notification email; so we list those 28 | revisions in full, below. 29 | 30 | - Log ----------------------------------------------------------------- 31 | 32 | commit $new_commit_hash 33 | Author: A U Thor 34 | Date: $new_commit_date 35 | 36 | rewind and continue branch on topic 37 | 38 | $new_commit_hash 39 | diff --git a/a b/a 40 | index 55da47f..a30ab5b 100644 41 | --- a/a 42 | +++ b/a 43 | @@ -1 +1 @@ 44 | -create branch 45 | +rewind and continue branch 46 | 47 | ----------------------------------------------------------------------- 48 | 49 | Summary of changes: 50 | a | 2 +- 51 | 1 files changed, 1 insertions(+), 1 deletions(-) 52 | -------------------------------------------------------------------------------- /tests/t2202-post-receive-email-branches.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | test_description='server post-receive email notification' 4 | 5 | . ./test-lib.sh 6 | 7 | export USER_EMAIL=author@example.com 8 | 9 | test_expect_success 'setup' ' 10 | echo "setup" >a && 11 | git add a && 12 | git commit -m "setup" && 13 | git clone -l . --bare server.git && 14 | rm -fr server.git/hooks && 15 | git remote add origin ./server.git && 16 | git config branch.master.remote origin && 17 | git config branch.master.merge refs/heads/master && 18 | GIT_DIR=./server.git git config hooks.post-receive-email.mailinglist commits@list.com && 19 | GIT_DIR=./server.git git config hooks.post-receive-email.debug true && 20 | echo cbas >./server.git/description 21 | ' 22 | 23 | install_post_receive_hook 'post-receive-email' 24 | 25 | test_expect_success 'create branch' ' 26 | git checkout -b topic master && 27 | echo "$test_name" >a && 28 | git commit -a -m "$test_name on topic" && 29 | prior_commit_hash=$(git rev-parse HEAD) && 30 | prior_commit_date=$(git log -n 1 --pretty=format:%cd HEAD) && 31 | 32 | echo "$test_name 2" >a && 33 | git commit -a -m "$test_name on topic 2 " && 34 | new_commit_hash=$(git rev-parse HEAD) && 35 | new_commit_abbrev=$(git rev-parse --short HEAD) && 36 | new_commit_date=$(git log -n 1 --pretty=format:%cd HEAD) && 37 | 38 | git push origin topic && 39 | 40 | interpolate ../t2202-1.txt 1.txt new_commit_hash new_commit_abbrev new_commit_date prior_commit_hash prior_commit_date && 41 | test_cmp 1.txt server.git/refs.heads.topic.out 42 | ' 43 | 44 | test_expect_success 'create branch with existing commits does not replay them' ' 45 | git checkout -b topic2 topic && 46 | existing_commit_hash=$(git rev-parse HEAD) && 47 | existing_commit_abbrev=$(git rev-parse --short HEAD) && 48 | existing_commit_date=$(git log -n 1 --pretty=format:%cd HEAD) && 49 | 50 | git push origin topic2 && 51 | 52 | interpolate ../t2202-3.txt 3.txt existing_commit_hash existing_commit_abbrev existing_commit_date && 53 | test_cmp 3.txt server.git/refs.heads.topic2.out 54 | ' 55 | 56 | test_expect_success 'update branch with existing commits does not replay them' ' 57 | # Put a commit on topic2, then fast foward topic to it 58 | git checkout topic2 && 59 | echo "$test_name" >a && 60 | git commit -a -m "$test_name on topic" && 61 | git push && 62 | 63 | git checkout topic && 64 | old_commit_hash=$(git rev-parse HEAD) && 65 | old_commit_abbrev=$(git rev-parse --short HEAD) && 66 | git merge topic2 && 67 | existing_commit_hash=$(git rev-parse HEAD) && 68 | existing_commit_abbrev=$(git rev-parse --short HEAD) && 69 | git push && 70 | 71 | interpolate ../t2202-4.txt 4.txt old_commit_hash old_commit_abbrev existing_commit_hash existing_commit_abbrev && 72 | test_cmp 4.txt server.git/refs.heads.topic.out 73 | ' 74 | 75 | test_expect_success 'rewind branch' ' 76 | git checkout topic && 77 | old_commit_hash=$(git rev-parse HEAD) && 78 | old_commit_abbrev=$(git rev-parse --short HEAD) && 79 | 80 | git reset --hard HEAD^ && 81 | git push --force && 82 | new_commit_hash=$(git rev-parse HEAD) && 83 | 84 | interpolate ../t2202-5.txt 5.txt old_commit_hash new_commit_hash old_commit_abbrev && 85 | test_cmp 5.txt server.git/refs.heads.topic.out 86 | ' 87 | 88 | test_expect_success 'rewind and continue branch' ' 89 | git checkout topic && 90 | old_commit_hash=$(git rev-parse HEAD) && 91 | old_commit_abbrev=$(git rev-parse --short HEAD) && 92 | 93 | git reset --hard HEAD^ && 94 | echo "$test_name" >a && 95 | git commit -a -m "$test_name on topic" && 96 | new_commit_hash=$(git rev-parse HEAD) && 97 | new_commit_abbrev=$(git rev-parse --short HEAD) && 98 | new_commit_date=$(git log -n 1 --pretty=format:%cd HEAD) && 99 | 100 | git push --force && 101 | interpolate ../t2202-6.txt 6.txt old_commit_hash new_commit_hash new_commit_date new_commit_abbrev old_commit_abbrev && 102 | test_cmp 6.txt server.git/refs.heads.topic.out 103 | ' 104 | 105 | test_expect_success 'delete branch' ' 106 | old_commit_hash=$(git rev-parse HEAD) && 107 | git push origin :refs/heads/topic && 108 | 109 | interpolate ../t2202-2.txt 2.txt old_commit_hash && 110 | test_cmp 2.txt server.git/refs.heads.topic.out 111 | ' 112 | 113 | test_done 114 | 115 | -------------------------------------------------------------------------------- /tests/t2203-1.txt: -------------------------------------------------------------------------------- 1 | From: author@example.com 2 | To: commits@list.com 3 | Subject: [cbas] topic1 branch updated. $new_commit_hash 4 | X-Git-Refname: refs/heads/topic1 5 | X-Git-Reftype: branch 6 | X-Git-Oldrev: $old_commit_hash 7 | X-Git-Newrev: $new_commit_hash 8 | 9 | The branch, topic1 has been updated 10 | via $new_commit_abbrev Merge branch 'stable' into topic1 11 | from $old_commit_abbrev move topic1 12 | 13 | Those revisions listed above that are new to this repository have 14 | not appeared on any other notification email; so we list those 15 | revisions in full, below. 16 | 17 | - Log ----------------------------------------------------------------- 18 | 19 | commit $new_commit_hash 20 | Merge: $old_commit_abbrev $second_stable_abbrev 21 | Author: A U Thor 22 | Date: $new_commit_date 23 | 24 | Merge branch 'stable' into topic1 25 | 26 | $new_commit_hash 27 | 28 | ----------------------------------------------------------------------- 29 | 30 | Summary of changes: 31 | a | 2 +- 32 | b | 2 +- 33 | c | 2 +- 34 | 3 files changed, 3 insertions(+), 3 deletions(-) 35 | -------------------------------------------------------------------------------- /tests/t2203-2.txt: -------------------------------------------------------------------------------- 1 | From: author@example.com 2 | To: commits@list.com 3 | Subject: [cbas] topic1 branch updated. $new_commit_hash 4 | X-Git-Refname: refs/heads/topic1 5 | X-Git-Reftype: branch 6 | X-Git-Oldrev: $old_commit_hash 7 | X-Git-Newrev: $new_commit_hash 8 | 9 | The branch, topic1 has been updated 10 | via $new_commit_abbrev Merge branch 'stable' into topic1 11 | from $old_commit_abbrev move topic1 12 | 13 | Those revisions listed above that are new to this repository have 14 | not appeared on any other notification email; so we list those 15 | revisions in full, below. 16 | 17 | - Log ----------------------------------------------------------------- 18 | 19 | commit $new_commit_hash 20 | Merge: $old_commit_abbrev $second_stable_abbrev 21 | Author: A U Thor 22 | Date: $new_commit_date 23 | 24 | Merge branch 'stable' into topic1 25 | 26 | Conflicts: 27 | a 28 | 29 | $new_commit_hash 30 | diff --cc a 31 | index a67d715,96d4816..01a17ba 32 | --- a/a 33 | +++ b/a 34 | @@@ -1,1 -1,1 +1,1 @@@ 35 | - merge in stable with conflict on topic1 36 | -merge in stable with conflict 2 37 | ++merge in stable with conflict 2 merged topic1 38 | 39 | ----------------------------------------------------------------------- 40 | 41 | Summary of changes: 42 | a | 2 +- 43 | b | 2 +- 44 | c | 2 +- 45 | 3 files changed, 3 insertions(+), 3 deletions(-) 46 | -------------------------------------------------------------------------------- /tests/t2203-post-receive-email-stable.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | test_description='server post-receive email notification and how it behaves in our stable-based envrionment' 4 | 5 | . ./test-lib.sh 6 | 7 | export USER_EMAIL=author@example.com 8 | 9 | test_expect_success 'setup' ' 10 | echo "setup" >a && 11 | echo "setup" >b && 12 | echo "setup" >c && 13 | git add a b c && 14 | git commit -m "setup" && 15 | git clone -l . --bare server.git && 16 | rm -fr server.git/hooks && 17 | git remote add origin ./server.git && 18 | git config branch.master.remote origin && 19 | git config branch.master.merge refs/heads/master && 20 | GIT_DIR=./server.git git config hooks.post-receive-email.mailinglist commits@list.com && 21 | GIT_DIR=./server.git git config hooks.post-receive-email.debug true && 22 | echo cbas >./server.git/description && 23 | 24 | git checkout -b stable && 25 | git push origin stable 26 | ' 27 | 28 | install_post_receive_hook 'post-receive-email' 29 | 30 | test_expect_success 'merge in stable' ' 31 | git checkout -b topic1 stable && 32 | echo "move" >a.topic1 && 33 | git add a.topic1 && 34 | git commit -a -m "move topic1" && 35 | git push origin topic1 && 36 | old_commit_hash=$(git rev-parse HEAD) && 37 | old_commit_abbrev=$(git rev-parse --short HEAD) && 38 | 39 | # Move stable 40 | git checkout stable && 41 | echo "$test_name 1" >a && 42 | echo "$test_name 1" >b && 43 | echo "$test_name 1" >c && 44 | git commit -a -m "move stable 1" && 45 | first_stable_hash=$(git rev-parse HEAD) && 46 | 47 | echo "$test_name 2" >a && 48 | echo "$test_name 2" >b && 49 | echo "$test_name 2" >c && 50 | git commit -a -m "move stable 2" && 51 | second_stable_abbrev=$(git rev-parse --short HEAD) && 52 | git push origin stable && 53 | 54 | # Merge stable 55 | git checkout topic1 && 56 | git merge stable && 57 | git push && 58 | 59 | new_commit_hash=$(git rev-parse HEAD) && 60 | new_commit_date=$(git log -n 1 --pretty=format:%cd HEAD) && 61 | new_commit_abbrev=$(git rev-parse --short HEAD) && 62 | 63 | interpolate ../t2203-1.txt 1.txt old_commit_hash old_commit_abbrev new_commit_hash new_commit_abbrev new_commit_date first_stable_hash second_stable_abbrev && 64 | test_cmp 1.txt server.git/refs.heads.topic1.out 65 | ' 66 | 67 | test_expect_success 'merge in stable with conflict' ' 68 | git checkout topic1 && 69 | echo "$test_name on topic1" >a && 70 | git commit -a -m "move topic1" && 71 | git push origin topic1 && 72 | old_commit_hash=$(git rev-parse HEAD) && 73 | old_commit_abbrev=$(git rev-parse --short HEAD) && 74 | 75 | # Move stable 76 | git checkout stable && 77 | echo "$test_name 1" >a && 78 | echo "$test_name 1" >b && 79 | echo "$test_name 1" >c && 80 | git commit -a -m "move stable 1" && 81 | first_stable_hash=$(git rev-parse HEAD) && 82 | 83 | echo "$test_name 2" >a && 84 | echo "$test_name 2" >b && 85 | echo "$test_name 2" >c && 86 | git commit -a -m "move stable 2" && 87 | second_stable_abbrev=$(git rev-parse --short HEAD) && 88 | git push origin stable && 89 | 90 | # Merge stable 91 | git checkout topic1 && 92 | ! git merge stable && 93 | echo "$test_name 2 merged topic1" >a && 94 | git add a && 95 | git commit -F .git/MERGE_MSG 96 | git push && 97 | 98 | new_commit_hash=$(git rev-parse HEAD) && 99 | new_commit_date=$(git log -n 1 --pretty=format:%cd HEAD) && 100 | new_commit_abbrev=$(git rev-parse --short HEAD) && 101 | 102 | interpolate ../t2203-2.txt 2.txt old_commit_hash old_commit_abbrev new_commit_hash new_commit_abbrev new_commit_date first_stable_hash second_stable_abbrev && 103 | test_cmp 2.txt server.git/refs.heads.topic1.out 104 | ' 105 | 106 | test_done 107 | 108 | -------------------------------------------------------------------------------- /tests/t2204-1.txt: -------------------------------------------------------------------------------- 1 | From: author@example.com 2 | To: commits@list.com 3 | Subject: [cbas] topic1 branch updated. $new_commit_hash 4 | X-Git-Refname: refs/heads/topic1 5 | X-Git-Reftype: branch 6 | X-Git-Oldrev: $old_commit_hash 7 | X-Git-Newrev: $new_commit_hash 8 | 9 | The branch, topic1 has been updated 10 | via $new_commit_abbrev resolved lines for merging stable into topic1 11 | from $old_commit_abbrev lines changed on topic1 12 | 13 | Those revisions listed above that are new to this repository have 14 | not appeared on any other notification email; so we list those 15 | revisions in full, below. 16 | 17 | - Log ----------------------------------------------------------------- 18 | 19 | commit $new_commit_hash 20 | Merge: $old_commit_abbrev $stable_abbrev 21 | Author: A U Thor 22 | Date: $new_commit_date 23 | 24 | resolved lines for merging stable into topic1 25 | 26 | $new_commit_hash 27 | diff --cc a 28 | index d288e69,a9ccdfe..3f45f93 29 | --- a/a 30 | +++ b/a 31 | @@@ -1,3 -1,3 +1,3 @@@ 32 | - line1.topic1 33 | - line2.topic1 34 | - line3.topic1 35 | -line1.stable 36 | ++line1.topic 37 | + line2.stable 38 | -line3.stable 39 | ++line3.resolved 40 | 41 | ----------------------------------------------------------------------- 42 | 43 | Summary of changes: 44 | a | 6 +++--- 45 | 1 files changed, 3 insertions(+), 3 deletions(-) 46 | -------------------------------------------------------------------------------- /tests/t2204-post-receive-email-conflicts.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | test_description='server post-receive email notification and how it behaves in our stable-based envrionment' 4 | 5 | . ./test-lib.sh 6 | 7 | export USER_EMAIL=author@example.com 8 | 9 | test_expect_success 'setup' ' 10 | echo "setup" >a && 11 | echo "setup" >b && 12 | echo "setup" >c && 13 | git add a b c && 14 | git commit -m "setup" && 15 | git clone -l . --bare server.git && 16 | rm -fr server.git/hooks && 17 | git remote add origin ./server.git && 18 | git config branch.master.remote origin && 19 | git config branch.master.merge refs/heads/master && 20 | GIT_DIR=./server.git git config hooks.post-receive-email.mailinglist commits@list.com && 21 | GIT_DIR=./server.git git config hooks.post-receive-email.debug true && 22 | echo cbas >./server.git/description && 23 | 24 | git checkout -b stable && 25 | git push origin stable 26 | ' 27 | 28 | install_post_receive_hook 'post-receive-email' 29 | 30 | test_expect_success 'conflict diff' ' 31 | git checkout stable && 32 | echo "line1" >a && 33 | echo "line2" >>a && 34 | echo "line3" >>a && 35 | git commit -a -m "lines" && 36 | git push origin stable && 37 | 38 | git checkout -b topic1 stable && 39 | echo "line1.topic1" >a && 40 | echo "line2.topic1" >>a && 41 | echo "line3.topic1" >>a && 42 | git commit -a -m "lines changed on topic1" && 43 | git push origin topic1 && 44 | 45 | old_commit_hash=$(git rev-parse HEAD) && 46 | old_commit_abbrev=$(git rev-parse --short HEAD) && 47 | 48 | # Move stable 49 | git checkout stable && 50 | echo "line1.stable" >a && 51 | echo "line2.stable" >>a && 52 | echo "line3.stable" >>a && 53 | git commit -a -m "lines changed on stable" && 54 | git push origin stable && 55 | 56 | stable_abbrev=$(git rev-parse --short HEAD) && 57 | 58 | git checkout topic1 && 59 | ! git merge stable && 60 | 61 | echo "line1.topic" >a && 62 | echo "line2.stable" >>a && 63 | echo "line3.resolved" >>a && 64 | git add a && 65 | git commit -a -m "resolved lines for merging stable into topic1" && 66 | second_stable_hash=$(git rev-parse HEAD) && 67 | git push origin topic1 && 68 | 69 | new_commit_hash=$(git rev-parse HEAD) && 70 | new_commit_date=$(git log -n 1 --pretty=format:%cd HEAD) && 71 | new_commit_abbrev=$(git rev-parse --short HEAD) && 72 | 73 | interpolate ../t2204-1.txt 1.txt old_commit_hash old_commit_abbrev new_commit_hash new_commit_abbrev new_commit_date stable_abbrev && 74 | test_cmp 1.txt server.git/refs.heads.topic1.out 75 | ' 76 | 77 | test_done 78 | 79 | -------------------------------------------------------------------------------- /tests/t2700-ensure-follows.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | test_description='server update ensure follows' 4 | 5 | . ./test-lib.sh 6 | 7 | test_expect_success 'setup' ' 8 | echo "setup" >a && 9 | git add a && 10 | git commit -m "setup" && 11 | git clone -l . --bare server.git && 12 | rm -fr server.git/hooks && 13 | git remote add origin ./server.git && 14 | git fetch 15 | ' 16 | 17 | install_update_hook 'update-ensure-follows' 18 | 19 | test_expect_success 'pushing stable works' ' 20 | git checkout -b stable && 21 | git push origin stable 22 | ' 23 | 24 | test_expect_success 'branch with unmoved stable is okay' ' 25 | GIT_DIR=server.git git config hooks.update-ensure-follows.branches stable && 26 | 27 | git checkout -b topic1 && 28 | echo "$test_name" >a.topic1 && 29 | git add a.topic1 && 30 | git commit -m "Add on topic1." && 31 | git push origin topic1 32 | ' 33 | 34 | test_expect_success 'branch with moved stable requires merge' ' 35 | git checkout stable && 36 | echo "$test_name" >a && 37 | git commit -a -m "Change on stable" && 38 | git push origin stable && 39 | 40 | git checkout topic1 && 41 | echo "$test_name" >a.topic1 && 42 | git commit -a -m "Change on topic1." && 43 | ! git push origin topic1 2>push.err && 44 | cat push.err | grep "You need to merge stable into topic1" && 45 | 46 | git merge stable && 47 | git push origin topic1 48 | ' 49 | 50 | test_expect_success 'branch with moved stable is told to update first' ' 51 | git checkout stable && 52 | echo "$test_name" >a && 53 | git commit -a -m "Change on stable" && 54 | git push origin stable && 55 | 56 | # Someone fixes stable first 57 | git clone server.git person2 && 58 | cd person2 && 59 | git checkout -f topic1 && 60 | git remote add server ../server.git && 61 | git merge origin/stable && 62 | git push server topic1 && 63 | cd .. && 64 | 65 | git checkout topic1 && 66 | echo "$test_name" >a.topic1 && 67 | git commit -a -m "Change on topic1." && 68 | ! git push --force origin topic1 2>push.err && 69 | cat push.err | grep "You need to update your local branch topic1" && 70 | 71 | # Now it will work as the teammate merged for us 72 | git pull origin topic1 && 73 | git push origin topic1 74 | ' 75 | 76 | test_expect_success 'branch with moved stable as second branch requires merge' ' 77 | GIT_DIR=server.git git config hooks.update-ensure-follows.branches "foo stable" && 78 | 79 | git checkout stable && 80 | echo "$test_name" >a && 81 | git commit -a -m "Change on stable" && 82 | git push origin stable && 83 | 84 | git checkout topic1 && 85 | echo "$test_name" >a.topic1 && 86 | git commit -a -m "Change on topic1." && 87 | ! git push origin topic1 2>push.err && 88 | cat push.err | grep "You need to merge stable into topic1" && 89 | 90 | git merge stable && 91 | git push origin topic1 92 | ' 93 | 94 | test_expect_success 'tag with moved stable is okay' ' 95 | git checkout stable && 96 | echo "$test_name" >a && 97 | git commit -a -m "Change on stable" && 98 | git push origin stable && 99 | 100 | git checkout topic1 && 101 | git tag topic1-tag1 102 | git push --tags 103 | ' 104 | 105 | test_expect_success 'branch deletion with moved stable is okay' ' 106 | git checkout stable && 107 | echo "$test_name" >a && 108 | git commit -a -m "Change on stable" && 109 | 110 | git push origin :topic1 111 | ' 112 | 113 | test_expect_success 'excused branch with moved stable is okay' ' 114 | git checkout -b topic2 stable && 115 | echo "$test_name" >a.topic2 && 116 | git add a.topic2 && 117 | git commit -m "Change on topic2" && 118 | git push origin topic2 && 119 | 120 | git checkout stable && 121 | echo "$test_name" >a && 122 | git commit -a -m "Change on stable" && 123 | git push origin stable && 124 | 125 | git checkout topic2 && 126 | echo "$test_name foo" >a.topic2 && 127 | git commit -a -m "Change on topic2 again" && 128 | ! git push origin topic2 && 129 | 130 | GIT_DIR=server.git git config hooks.update-ensure-follows.excused topic2 && 131 | 132 | git push origin topic2 133 | ' 134 | 135 | test_expect_success 'new branch without stable gets nicer error' ' 136 | git checkout -b topic3 stable && 137 | echo "$test_name" >a.topic3 && 138 | git add a.topic3 && 139 | git commit -m "Change on topic3" && 140 | 141 | git checkout stable && 142 | echo "$test_name" >a && 143 | git commit -a -m "Change on stable" && 144 | git push origin stable && 145 | 146 | git checkout topic3 && 147 | ! git push origin topic3 2>push.err && 148 | grep "You need to merge stable into topic3" push.err && 149 | 150 | git merge stable && 151 | git push origin topic3 152 | ' 153 | 154 | test_done 155 | 156 | -------------------------------------------------------------------------------- /tests/t2800-post-receive-gitconfig.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | test_description='server update git config' 4 | 5 | . ./test-lib.sh 6 | 7 | test_expect_success 'setup' ' 8 | echo "setup" >a && 9 | git add a && 10 | git commit -m "setup" && 11 | git clone -l . --bare server.git && 12 | rm -fr server.git/hooks && 13 | git remote add origin ./server.git && 14 | git config branch.master.remote origin && 15 | git config branch.master.merge refs/heads/master && 16 | git fetch 17 | ' 18 | 19 | install_post_receive_hook 'post-receive-gitconfig' 20 | 21 | test_expect_success 'pushing initial value works' ' 22 | ! GIT_DIR=server.git git config --list | grep foo && 23 | 24 | ../../scripts/create-gitconfig && 25 | git checkout gitconfig && 26 | echo "foo.foo=bar" > config && 27 | git commit -a -m "Set foo.foo=bar." 28 | git push origin gitconfig 29 | 30 | GIT_DIR=server.git git config --list | grep foo 31 | ' 32 | 33 | test_expect_success 'pushing locked works' ' 34 | ! test -f server.git/locked && 35 | 36 | git checkout gitconfig && 37 | echo "foo" > locked && 38 | git add locked && 39 | git commit -m "Add locked" 40 | git push origin gitconfig 41 | 42 | test -f server.git/locked 43 | ' 44 | 45 | test_done 46 | 47 | -------------------------------------------------------------------------------- /tests/t2801-post-receive-gitconfig-hooks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | test_description='server update git config' 4 | 5 | . ./test-lib.sh 6 | 7 | test_expect_success 'setup' ' 8 | echo "setup" >a && 9 | git add a && 10 | git commit -m "setup" && 11 | git clone -l . --bare server.git && 12 | rm -fr server.git/hooks && 13 | git remote add origin ./server.git 14 | ' 15 | 16 | install_post_receive_hook 'post-receive-gitconfig' 17 | 18 | test_expect_success 'adding hook' ' 19 | ls server.git/hooks | grep post-receive && 20 | ../../scripts/create-gitconfig && 21 | git checkout gitconfig && 22 | 23 | mkdir hooks && 24 | cd hooks && 25 | echo "#!/bin/bash" > post-receive && 26 | echo "../../../server/post-receive-gitconfig" >> post-receive && 27 | echo "echo barbar" >> post-receive && 28 | echo "#!/bin/bash" > update && 29 | echo "echo foofoo" >> update && 30 | git add post-receive && 31 | git add update && 32 | git commit -m "added post-receive and update" && 33 | git push origin gitconfig && 34 | cd .. && 35 | 36 | cat server.git/hooks/post-receive | grep barbar && 37 | cat server.git/hooks/update | grep foofoo 38 | ' 39 | 40 | test_expect_success 'changing hook' ' 41 | echo "#!/bin/bash" > hooks/update && 42 | echo "echo lala" >> hooks/update && 43 | git commit -a -m "changed update" && 44 | git push origin gitconfig && 45 | 46 | cat server.git/hooks/post-receive | grep barbar && 47 | ! cat server.git/hooks/update | grep barbar && 48 | cat server.git/hooks/update | grep lala 49 | ' 50 | 51 | test_expect_success 'removing hook does not work' ' 52 | git rm hooks/update && 53 | git commit -m "removed update" && 54 | git push origin gitconfig && 55 | 56 | ls server.git/hooks | grep post-receive 57 | ls server.git/hooks | grep update 58 | ' 59 | 60 | test_done 61 | 62 | -------------------------------------------------------------------------------- /tests/t2900-update-lock-check.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | test_description='server update lock check' 4 | 5 | . ./test-lib.sh 6 | 7 | test_expect_success 'setup' ' 8 | echo "setup" >a && 9 | git add a && 10 | git commit -m "setup" && 11 | git clone -l . --bare server.git && 12 | rm -fr server.git/hooks && 13 | git remote add origin ./server.git && 14 | git config branch.master.remote origin && 15 | git config branch.master.merge refs/heads/master && 16 | git fetch 17 | ' 18 | 19 | install_update_hook 'update-lock-check' 20 | 21 | test_expect_success 'locked branch is rejected' ' 22 | echo master >> server.git/locked && 23 | 24 | echo "$test_name" >a && 25 | git commit -a -m "changed" && 26 | ! git push 2>push.err && 27 | cat push.err | grep "Branch master is locked" 28 | ' 29 | 30 | test_expect_success 'locked branch is rejected with multiple branches set' ' 31 | echo foo >> server.git/locked && 32 | echo bar >> server.git/locked && 33 | 34 | echo "$test_name" >a && 35 | git commit -a -m "changed" && 36 | ! git push 2>push.err && 37 | cat push.err | grep "Branch master is locked" 38 | ' 39 | 40 | test_expect_success 'preserved branch cannot be deleted' ' 41 | echo > server.git/locked && 42 | git push origin master:master2 && 43 | echo master2 > server.git/preserved && 44 | 45 | ! git push origin :master2 2>push.err && 46 | cat push.err | grep "Branch master2 cannot be deleted" 47 | ' 48 | 49 | test_done 50 | 51 | -------------------------------------------------------------------------------- /tests/t3000-post-receive-trac.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | test_description='server update lock check' 4 | 5 | . ./test-lib.sh 6 | 7 | export PYTHON=echo 8 | export TRAC_ENV=/foo/trac 9 | 10 | test_expect_success 'setup' ' 11 | echo "setup" >a && 12 | git add a && 13 | git commit -m "setup" && 14 | git clone -l . --bare server.git && 15 | rm -fr server.git/hooks && 16 | git remote add origin ./server.git 17 | ' 18 | 19 | install_post_receive_hook 'post-receive-trac' 20 | 21 | test_expect_success 'new branch' ' 22 | git checkout -b topic1 master && 23 | echo "$test_name" >a && 24 | git commit -a -m "changed on topic1" && 25 | new_commit_hash=$(git rev-parse HEAD) && 26 | git push origin topic1 2>push.err && 27 | cat push.err | grep "/foo/trac topic1 $new_commit_hash $new_commit_hash $new_commit_hash" 28 | ' 29 | 30 | test_expect_success 'new branch with already existing does not double tap' ' 31 | git checkout -b topic2 topic1 && 32 | existing_commit_hash=$(git rev-parse HEAD) && 33 | git push origin topic2 2>push.err && 34 | ! cat push.err | grep "/foo/trac topic2" 35 | ' 36 | 37 | test_expect_success 'update branch' ' 38 | # Already on topic2 39 | echo "$test_name" >a && 40 | git commit -a -m "changed on topic2" && 41 | new_commit_hash=$(git rev-parse HEAD) && 42 | git push origin topic2 2>push.err && 43 | cat push.err | grep "/foo/trac topic2 $new_commit_hash $new_commit_hash $new_commit_hash" 44 | ' 45 | 46 | test_expect_success 'update branch to an already published commit does not double tap' ' 47 | # Make topic1 catch up to topic2, which will be a fast forward that does need re-tapped 48 | git checkout topic2 && 49 | topic2_hash=$(git rev-parse HEAD) && 50 | 51 | git checkout topic1 && 52 | git merge topic2 && 53 | topic1_hash=$(git rev-parse HEAD) && 54 | 55 | git push 2>push.err && 56 | 57 | ! cat push.err | grep "/foo/trac topic2" 58 | ! cat push.err | grep "/foo/trac topic1" 59 | ' 60 | 61 | test_expect_success 'update branch with abbreviation' ' 62 | git checkout topic2 63 | git tag -m "release1" release1 && 64 | git push --tags && 65 | 66 | echo "$test_name" >a && 67 | git commit -a -m "changed on topic2" && 68 | new_commit_describe=$(git describe HEAD) && 69 | new_commit_hash=$(git rev-parse HEAD) && 70 | git push origin topic2 2>push.err && 71 | cat push.err | grep "/foo/trac topic2 $new_commit_describe $new_commit_describe $new_commit_hash" 72 | ' 73 | 74 | test_expect_success 'update branch with abbreviation and two commits' ' 75 | echo "$test_name 1" >a && 76 | git commit -a -m "changed on topic2 1" && 77 | first_commit_describe=$(git describe HEAD) && 78 | first_commit_hash=$(git rev-parse HEAD) && 79 | 80 | echo "$test_name 2" >a && 81 | git commit -a -m "changed on topic2 2" && 82 | second_commit_describe=$(git describe HEAD) && 83 | second_commit_hash=$(git rev-parse HEAD) && 84 | 85 | git push origin topic2 2>push.err && 86 | cat push.err | grep "/foo/trac topic2 $first_commit_describe $first_commit_describe $first_commit_hash" && 87 | cat push.err | grep "/foo/trac topic2 $second_commit_describe $second_commit_describe $second_commit_hash" 88 | ' 89 | 90 | test_done 91 | 92 | -------------------------------------------------------------------------------- /tests/t3001-post-receive-trac-with-commitnumbers.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | test_description='server post receive trac with commit numbers' 4 | 5 | . ./test-lib.sh 6 | 7 | export PYTHON=echo 8 | export TRAC_ENV=/foo/trac 9 | 10 | test_expect_success 'setup' ' 11 | echo "setup" >a && 12 | git add a && 13 | git commit -m "setup" && 14 | git clone -l . --bare server.git && 15 | rm -fr server.git/hooks && 16 | git remote add origin ./server.git 17 | ' 18 | 19 | install_post_receive_hook 'post-receive-commitnumbers' 'post-receive-trac' 20 | 21 | test_expect_success 'new branch' ' 22 | git checkout -b topic1 master && 23 | echo "$test_name" >a && 24 | git commit -a -m "changed on topic1" && 25 | new_commit_hash=$(git rev-parse HEAD) && 26 | git push origin topic1 2>push.err && 27 | cat push.err | grep "/foo/trac topic1 $new_commit_hash r/1 $new_commit_hash" 28 | ' 29 | 30 | test_expect_success 'new branch with already existing does not double tap' ' 31 | git checkout -b topic2 topic1 && 32 | existing_commit_hash=$(git rev-parse HEAD) && 33 | git push origin topic2 2>push.err && 34 | ! cat push.err | grep "/foo/trac topic2" 35 | ' 36 | 37 | test_expect_success 'update branch' ' 38 | # Already on topic2 39 | echo "$test_name" >a && 40 | git commit -a -m "changed on topic2" && 41 | new_commit_hash=$(git rev-parse HEAD) && 42 | git push origin topic2 2>push.err && 43 | cat push.err | grep "/foo/trac topic2 $new_commit_hash r/2 $new_commit_hash" 44 | ' 45 | 46 | test_expect_success 'update branch to an already published commit does not double tap' ' 47 | # Make topic1 catch up to topic2, which will be a fast forward that does need re-tapped 48 | git checkout topic2 && 49 | topic2_hash=$(git rev-parse HEAD) && 50 | 51 | git checkout topic1 && 52 | git merge topic2 && 53 | topic1_hash=$(git rev-parse HEAD) && 54 | 55 | git push 2>push.err && 56 | 57 | ! cat push.err | grep "/foo/trac topic2" 58 | ! cat push.err | grep "/foo/trac topic1" 59 | ' 60 | 61 | test_expect_success 'update branch with abbreviation' ' 62 | git checkout topic2 63 | git tag -m "release1" release1 && 64 | git push --tags && 65 | 66 | echo "$test_name" >a && 67 | git commit -a -m "changed on topic2" && 68 | new_commit_describe=$(git describe HEAD) && 69 | new_commit_hash=$(git rev-parse HEAD) && 70 | git push origin topic2 2>push.err && 71 | cat push.err | grep "/foo/trac topic2 $new_commit_describe r/3 $new_commit_hash" 72 | ' 73 | 74 | test_expect_success 'update branch with abbreviation and two commits' ' 75 | echo "$test_name 1" >a && 76 | git commit -a -m "changed on topic2 1" && 77 | first_commit_describe=$(git describe HEAD) && 78 | first_commit_hash=$(git rev-parse HEAD) && 79 | 80 | echo "$test_name 2" >a && 81 | git commit -a -m "changed on topic2 2" && 82 | second_commit_describe=$(git describe HEAD) && 83 | second_commit_hash=$(git rev-parse HEAD) && 84 | 85 | git push origin topic2 2>push.err && 86 | cat push.err | grep "/foo/trac topic2 $first_commit_describe r/4 $first_commit_hash" && 87 | cat push.err | grep "/foo/trac topic2 $second_commit_describe r/5 $second_commit_hash" 88 | ' 89 | 90 | test_done 91 | 92 | -------------------------------------------------------------------------------- /tests/t3100-update-allow-tags-branches.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | test_description='server update allow tags and branches' 4 | 5 | . ./test-lib.sh 6 | 7 | test_expect_success 'setup' ' 8 | echo "setup" >a && 9 | git add a && 10 | git commit -m "setup" && 11 | git clone -l . --bare server.git && 12 | rm -fr server.git/hooks && 13 | git remote add origin ./server.git 14 | ' 15 | 16 | install_update_hook 'update-allow-tags-branches' 17 | 18 | test_expect_success 'push only tag fails' ' 19 | echo "$test_name" >a && 20 | git commit -a -m "$test_name moved master" && 21 | git tag -a -m "tagged move as r1" r1 && 22 | ! git push --tags 2>push.err && 23 | cat push.err | grep "The tag r1 is not included in any branch" && 24 | 25 | # But now it works if we push the commit first 26 | git push && 27 | git push --tags 28 | ' 29 | 30 | test_expect_success 'push works if done at the same time' ' 31 | echo "$test_name" >a && 32 | git commit -a -m "$test_name moved master" && 33 | git tag -a -m "tagged move as r2" r2 && 34 | git push origin master r2 35 | ' 36 | 37 | test_expect_success 'moving branch back and deleting tag works' ' 38 | GIT_DIR=./server.git git config hooks.update-allow-tags-branches.deletetag true 39 | git reset --hard HEAD^ && 40 | git push --force origin master:master :r2 41 | ' 42 | 43 | test_done 44 | 45 | -------------------------------------------------------------------------------- /tests/t3200-post-receive-commitnumbers.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | test_description='server assign commit numbers' 4 | 5 | . ./test-lib.sh 6 | 7 | test_expect_success 'setup' ' 8 | echo "setup" >a && 9 | git add a && 10 | git commit -m "setup" && 11 | git clone -l . --bare server.git && 12 | rm -fr server.git/hooks && 13 | git remote add origin ./server.git && 14 | git config branch.master.remote origin && 15 | git config branch.master.merge refs/heads/master && 16 | git fetch 17 | ' 18 | 19 | install_post_receive_hook 'post-receive-commitnumbers' 20 | 21 | test_expect_success 'assign one new commit' ' 22 | git checkout master && 23 | echo "$test_name" >a && 24 | git commit -a -m "changed a" && 25 | git push origin master && 26 | git fetch && 27 | 28 | test "$(git rev-parse HEAD)" = "$(git rev-parse r/1)" && 29 | test "$(git describe --tags)" = "r/1" && 30 | test "$(git rev-parse HEAD) refs/heads/master" = "$(cat server.git/commitnumbers)" 31 | ' 32 | 33 | test_expect_success 'assign two new commits' ' 34 | echo "$test_name first" >a && 35 | git commit -a -m "changed a first" && 36 | echo "$test_name second" >a && 37 | git commit -a -m "changed a second" && 38 | git push origin master && 39 | git fetch && 40 | 41 | test "$(git rev-parse HEAD)" = "$(git rev-parse r/3)" && 42 | test "$(git describe --tags)" = "r/3" && 43 | 44 | test "$(git rev-parse HEAD^)" = "$(git rev-parse r/2)" && 45 | test "$(git describe --tags HEAD^)" = "r/2" 46 | ' 47 | 48 | test_expect_success 'pushing commits to a new branch does not reassign' ' 49 | git checkout -b topica && 50 | echo "$test_name" && 51 | git push origin topica && 52 | git fetch && 53 | 54 | ! git rev-parse r/4 55 | ' 56 | 57 | test_done 58 | 59 | -------------------------------------------------------------------------------- /tests/t5000-checkout.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | test_description='script checkout' 4 | 5 | . ./test-lib.sh 6 | 7 | export PATH=$PATH:../../scripts 8 | 9 | test_expect_success 'setup' ' 10 | echo "setup" >a && 11 | git add a && 12 | git commit -m "setup" && 13 | git clone -l . --bare server.git && 14 | rm -fr server.git/hooks && 15 | git remote add origin ./server.git 16 | git checkout -b stable && 17 | git push origin stable 18 | ' 19 | 20 | test_expect_success 'checkout a new branch clones stable' ' 21 | checkout topic1 && 22 | git branch | grep topic1 && 23 | git branch -r | grep origin/topic1 && 24 | git config --list | grep "branch.topic1.merge=refs/heads/topic1" 25 | ' 26 | 27 | test_expect_success 'checkout an existing remote branch' ' 28 | git clone server.git person2 && 29 | cd person2 && 30 | git checkout -b topic2 origin/stable && 31 | echo "$test_name on server" >a && 32 | git commit -a -m "Made topic2 on server" && 33 | git push origin topic2 34 | cd .. && 35 | 36 | ! git branch | grep topic2 && 37 | checkout topic2 && 38 | git branch | grep topic2 && 39 | git branch -r | grep origin/topic2 && 40 | git config --list | grep "branch.topic2.merge=refs/heads/topic2" && 41 | 42 | echo "$test_name on client" >a && 43 | git commit -a -m "Move topic2 on client" && 44 | git push origin topic2 45 | ' 46 | 47 | test_expect_success 'checkout an existing local branch' ' 48 | checkout topic1 49 | ' 50 | 51 | test_expect_success 'checkout a revision does not create a new branch' ' 52 | echo "$test_name" >a && 53 | git commit -a -m "$test_name" && 54 | 55 | prior=$(git rev-parse HEAD^) && 56 | checkout $prior && 57 | git branch | grep "no branch" 58 | ' 59 | 60 | test_done 61 | 62 | -------------------------------------------------------------------------------- /tests/t5100-push.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | test_description='script create branch' 4 | 5 | . ./test-lib.sh 6 | 7 | export PATH=$PATH:../../scripts 8 | 9 | test_expect_success 'setup' ' 10 | echo "setup" >a && 11 | git add a && 12 | git commit -m "setup" && 13 | git clone ./. server && 14 | rm -fr server/.git/hooks && 15 | git remote add origin ./server && 16 | git config branch.master.remote origin && 17 | git config branch.master.merge refs/heads/master && 18 | git fetch && 19 | 20 | git checkout -b stable && 21 | git push origin stable 22 | ' 23 | 24 | test_expect_success 'push only does one branch' ' 25 | checkout topic1 && 26 | echo "$test_name" >a && 27 | git commit -a -m "move topic1" && 28 | git rev-parse HEAD >head.topic1 && 29 | 30 | checkout topic2 && 31 | echo "$test_name" >a && 32 | git commit -a -m "move topic2" && 33 | git rev-parse HEAD >head.topic2 && 34 | 35 | push && 36 | git rev-parse origin/topic2 >head.origin.topic2 && 37 | git rev-parse origin/topic1 >head.origin.topic1 && 38 | 39 | test_cmp head.topic2 head.origin.topic2 && 40 | ! test_cmp head.topic2 head.origin.topic1 41 | ' 42 | 43 | test_done 44 | 45 | -------------------------------------------------------------------------------- /tests/t5200-pull.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | test_description='script pull' 4 | 5 | . ./test-lib.sh 6 | 7 | export PATH=$PATH:../../scripts 8 | 9 | test_expect_success 'setup' ' 10 | echo "setup" >a && 11 | git add a && 12 | git commit -m "setup" && 13 | git clone -l . --bare server.git && 14 | rm -fr server.git/hooks && 15 | git remote add origin ./server.git && 16 | git checkout -b stable && 17 | git push origin stable 18 | ' 19 | 20 | test_expect_success 'pull does a rebase' ' 21 | checkout topic1 && 22 | echo "$test_name" >a.topic1 && 23 | git add a.topic1 && 24 | git commit -m "move topic1" && 25 | 26 | # Move topic1 on the server 27 | git clone server.git person2 && 28 | cd person2 && 29 | git checkout topic1 && 30 | echo "$test_name" >a && 31 | git commit -a -m "move topic1 on the server" && 32 | git push origin && 33 | cd .. && 34 | 35 | # Only one parent 36 | pull && 37 | test 1 = $(git cat-file commit $(git rev-parse HEAD) | grep parent | wc -l) 38 | ' 39 | 40 | test_expect_success 'pull does a rebase but does not fuck up merges' ' 41 | checkout topic2 && 42 | echo "$test_name on topic2" >a.topic2 && 43 | git add a.topic2 && 44 | git commit -a -m "create topic2" && 45 | git push origin topic2 && 46 | 47 | # Move stable 48 | git checkout stable && 49 | echo "$test_name on stable" >a && 50 | git commit -a -m "move stable that will not be replayed" && 51 | git push origin stable && 52 | 53 | # And merge stable into topic2 54 | git checkout topic2 && 55 | git merge stable && 56 | 57 | # Move topic2 on the server 58 | cd person2 && 59 | git fetch && 60 | git checkout -b topic2 origin/topic2 && 61 | echo "$test_name" >a.topic2.server && 62 | git add a.topic2.server && 63 | git commit -m "move topic2 on the server" && 64 | git push origin && 65 | cd .. && 66 | 67 | # Merge stable locally too--should conflict 68 | git checkout topic2 && 69 | pull && 70 | test 1 = $(git rev-list --all --pretty=oneline | grep "replayed" | wc -l) && 71 | push 72 | ' 73 | 74 | test_expect_success 'pull moves when we have no local changes' ' 75 | git checkout topic2 && 76 | 77 | # Move topic2 on the server 78 | cd person2 && 79 | git pull && 80 | echo "$test_name" > a.topic2.server && 81 | git commit -a -m "move topic2 on the server" && 82 | git push origin && 83 | cd .. && 84 | 85 | pull && 86 | test $(git rev-parse HEAD) = $(git rev-parse origin/topic2) 87 | ' 88 | 89 | test_done 90 | 91 | -------------------------------------------------------------------------------- /tests/t5400-refollow.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | test_description='script refollow' 4 | 5 | . ./test-lib.sh 6 | 7 | export PATH=$PATH:../../scripts 8 | 9 | test_expect_success 'setup' ' 10 | echo "setup" >a && 11 | git add a && 12 | git commit -m "setup" && 13 | git clone -l . --bare server.git && 14 | rm -fr server.git/hooks && 15 | git remote add origin ./server.git && 16 | git checkout -b stable && 17 | git push origin stable 18 | ' 19 | 20 | test_expect_success 'setup gitconfig' ' 21 | create-gitconfig && 22 | git checkout gitconfig && 23 | echo "hooks.update-ensure-follows.branches=stable" >>config && 24 | echo "hooks.update-ensure-follows.excused=master gitconfig" >>config && 25 | git commit -a -m "enable update-ensure-follows" && 26 | git push origin gitconfig 27 | ' 28 | 29 | test_expect_success 'make topic1 then move stable' ' 30 | git checkout -b topic1 stable && 31 | echo "$test_name" >a.topic1 && 32 | git add a.topic1 && 33 | git commit -m "$test_name on topic1" && 34 | git push origin topic1 && 35 | 36 | git checkout stable && 37 | echo "$test_name" >a && 38 | git commit -a -m "$test_name on stable" && 39 | git push 40 | ' 41 | 42 | test_expect_success 'refollow fails with dirty index' ' 43 | echo "$test_name" >a && 44 | git add a && 45 | ! refollow 2>refollow.err && 46 | cat refollow.err | grep "refusing to refollow--your index is not clean" && 47 | ! git reset a 48 | ' 49 | 50 | test_expect_success 'refollow topic1 onto stable' ' 51 | echo "$test_name" >a && 52 | git commit -a -m "move stable" && 53 | git push origin stable && 54 | refollow >refollow.out && 55 | cat refollow.out | grep "Merging stable into topic1...succeeded" 56 | 57 | git checkout topic1 && 58 | git pull origin topic1 && 59 | cat a | grep "$test_name" 60 | ' 61 | 62 | test_expect_success 'refollow does not double tap' ' 63 | # Still on topic1 64 | head=$(git rev-parse HEAD) && 65 | refollow && 66 | git pull origin topic1 && 67 | git rev-parse HEAD | grep $head 68 | ' 69 | 70 | test_expect_success 'refollow respects excused' ' 71 | git checkout gitconfig && 72 | head=$(git rev-parse HEAD) && 73 | 74 | git checkout stable && 75 | echo "$test_name" >a && 76 | git commit -a -m "move stable" && 77 | git push origin stable && 78 | 79 | refollow && 80 | 81 | git checkout gitconfig && 82 | git pull origin gitconfig && 83 | git rev-parse HEAD | grep $head 84 | ' 85 | 86 | test_expect_success 'refollow continues on conflict' ' 87 | git checkout -b topic2 stable && 88 | echo "$test_name" >a && 89 | git commit -a -m "create topic2" && 90 | git push origin topic2 && 91 | 92 | git checkout stable && 93 | echo "$test_name" >a && 94 | git commit -a -m "move stable" && 95 | git push origin stable && 96 | 97 | refollow > refollow.out && 98 | cat refollow.out | grep "Merging stable into topic1...succeeded" 99 | cat refollow.out | grep "Merging stable into topic2...failed merge" 100 | ' 101 | 102 | test_done 103 | 104 | -------------------------------------------------------------------------------- /tests/t9000-notes.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | test_description='sanity check of commands listed in GitNotes' 4 | 5 | . ./test-lib.sh 6 | 7 | test_expect_success 'setup' ' 8 | echo setup >a && 9 | git add a && 10 | git commit -m "a" && 11 | git clone ./. server && 12 | git remote add origin ./server && 13 | rm -fr server/.git/hooks 14 | 15 | git checkout -b stable && 16 | git push origin stable && 17 | git config --add branch.stable.remote origin && 18 | git config --add branch.stable.merge refs/heads/stable 19 | ' 20 | 21 | test_expect_success 'make a new local/remote branch' ' 22 | git fetch && 23 | git checkout -b hotfix2 origin/stable && 24 | git push origin hotfix2 && 25 | 26 | # Initially we are still setup to pull from stable 27 | git config --list | grep branch.hotfix2.merge=refs/heads/stable && 28 | git config --replace-all branch.hotfix2.merge refs/heads/hotfix2 && 29 | 30 | # But we want to pull from the new hotfix2 instead 31 | git config --list | grep branch.hotfix2.merge=refs/heads/hotfix2 32 | ' 33 | 34 | test_expect_success 'make a new local branch' ' 35 | # Remove the old hotfix2 local branch 36 | git checkout origin/hotfix2 && 37 | git branch -d hotfix2 && 38 | 39 | # Now back to the real commands 40 | git fetch && 41 | git checkout -b hotfix2 origin/hotfix2 42 | ' 43 | 44 | test_done 45 | 46 | -------------------------------------------------------------------------------- /tests/test-lib.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Copyright (c) 2005 Junio C Hamano 4 | # 5 | 6 | # Keep the original TERM for say_color 7 | ORIGINAL_TERM=$TERM 8 | 9 | # For repeatability, reset the environment to known value. 10 | LANG=C 11 | LC_ALL=C 12 | PAGER=cat 13 | TZ=UTC 14 | TERM=dumb 15 | export LANG LC_ALL PAGER TERM TZ 16 | EDITOR=: 17 | VISUAL=: 18 | unset GIT_EDITOR 19 | unset AUTHOR_DATE 20 | unset AUTHOR_EMAIL 21 | unset AUTHOR_NAME 22 | unset COMMIT_AUTHOR_EMAIL 23 | unset COMMIT_AUTHOR_NAME 24 | unset EMAIL 25 | unset GIT_ALTERNATE_OBJECT_DIRECTORIES 26 | unset GIT_AUTHOR_DATE 27 | GIT_AUTHOR_EMAIL=author@example.com 28 | GIT_AUTHOR_NAME='A U Thor' 29 | unset GIT_COMMITTER_DATE 30 | GIT_COMMITTER_EMAIL=committer@example.com 31 | GIT_COMMITTER_NAME='C O Mitter' 32 | unset GIT_DIFF_OPTS 33 | unset GIT_DIR 34 | unset GIT_WORK_TREE 35 | unset GIT_EXTERNAL_DIFF 36 | unset GIT_INDEX_FILE 37 | unset GIT_OBJECT_DIRECTORY 38 | unset SHA1_FILE_DIRECTORIES 39 | unset SHA1_FILE_DIRECTORY 40 | GIT_MERGE_VERBOSITY=5 41 | export GIT_MERGE_VERBOSITY 42 | export GIT_AUTHOR_EMAIL GIT_AUTHOR_NAME 43 | export GIT_COMMITTER_EMAIL GIT_COMMITTER_NAME 44 | export EDITOR VISUAL 45 | GIT_TEST_CMP=${GIT_TEST_CMP:-diff -u} 46 | 47 | # Protect ourselves from common misconfiguration to export 48 | # CDPATH into the environment 49 | unset CDPATH 50 | 51 | case $(echo $GIT_TRACE |tr "[A-Z]" "[a-z]") in 52 | 1|2|true) 53 | echo "* warning: Some tests will not work if GIT_TRACE" \ 54 | "is set as to trace on STDERR ! *" 55 | echo "* warning: Please set GIT_TRACE to something" \ 56 | "other than 1, 2 or true ! *" 57 | ;; 58 | esac 59 | 60 | # Each test should start with something like this, after copyright notices: 61 | # 62 | # test_description='Description of this test... 63 | # This test checks if command xyzzy does the right thing... 64 | # ' 65 | # . ./test-lib.sh 66 | [ "x$ORIGINAL_TERM" != "xdumb" ] && ( 67 | TERM=$ORIGINAL_TERM && 68 | export TERM && 69 | [ -t 1 ] && 70 | tput bold >/dev/null 2>&1 && 71 | tput setaf 1 >/dev/null 2>&1 && 72 | tput sgr0 >/dev/null 2>&1 73 | ) && 74 | color=t 75 | 76 | while test "$#" -ne 0 77 | do 78 | case "$1" in 79 | -d|--d|--de|--deb|--debu|--debug) 80 | debug=t; shift ;; 81 | -i|--i|--im|--imm|--imme|--immed|--immedi|--immedia|--immediat|--immediate) 82 | immediate=t; shift ;; 83 | -h|--h|--he|--hel|--help) 84 | help=t; shift ;; 85 | -v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose) 86 | verbose=t; shift ;; 87 | -q|--q|--qu|--qui|--quie|--quiet) 88 | quiet=t; shift ;; 89 | --no-color) 90 | color=; shift ;; 91 | --no-python) 92 | # noop now... 93 | shift ;; 94 | *) 95 | break ;; 96 | esac 97 | done 98 | 99 | if test -n "$color"; then 100 | say_color () { 101 | ( 102 | TERM=$ORIGINAL_TERM 103 | export TERM 104 | case "$1" in 105 | error) tput bold; tput setaf 1;; # bold red 106 | skip) tput bold; tput setaf 2;; # bold green 107 | pass) tput setaf 2;; # green 108 | info) tput setaf 3;; # brown 109 | *) test -n "$quiet" && return;; 110 | esac 111 | shift 112 | echo "* $*" 113 | tput sgr0 114 | ) 115 | } 116 | else 117 | say_color() { 118 | test -z "$1" && test -n "$quiet" && return 119 | shift 120 | echo "* $*" 121 | } 122 | fi 123 | 124 | error () { 125 | say_color error "error: $*" 126 | trap - exit 127 | exit 1 128 | } 129 | 130 | say () { 131 | say_color info "$*" 132 | } 133 | 134 | test "${test_description}" != "" || 135 | error "Test script did not set test_description." 136 | 137 | if test "$help" = "t" 138 | then 139 | echo "$test_description" 140 | exit 0 141 | fi 142 | 143 | exec 5>&1 144 | if test "$verbose" = "t" 145 | then 146 | exec 4>&2 3>&1 147 | else 148 | exec 4>/dev/null 3>/dev/null 149 | fi 150 | 151 | test_failure=0 152 | test_count=0 153 | test_fixed=0 154 | test_broken=0 155 | 156 | die () { 157 | echo >&5 "FATAL: Unexpected exit with code $?" 158 | exit 1 159 | } 160 | 161 | trap 'die' exit 162 | 163 | # The semantics of the editor variables are that of invoking 164 | # sh -c "$EDITOR \"$@\"" files ... 165 | # 166 | # If our trash directory contains shell metacharacters, they will be 167 | # interpreted if we just set $EDITOR directly, so do a little dance with 168 | # environment variables to work around this. 169 | # 170 | # In particular, quoting isn't enough, as the path may contain the same quote 171 | # that we're using. 172 | test_set_editor () { 173 | FAKE_EDITOR="$1" 174 | export FAKE_EDITOR 175 | VISUAL='"$FAKE_EDITOR"' 176 | export VISUAL 177 | } 178 | 179 | test_tick () { 180 | if test -z "${test_tick+set}" 181 | then 182 | test_tick=1112911993 183 | else 184 | test_tick=$(($test_tick + 60)) 185 | fi 186 | GIT_COMMITTER_DATE="$test_tick -0700" 187 | GIT_AUTHOR_DATE="$test_tick -0700" 188 | export GIT_COMMITTER_DATE GIT_AUTHOR_DATE 189 | } 190 | 191 | # You are not expected to call test_ok_ and test_failure_ directly, use 192 | # the text_expect_* functions instead. 193 | 194 | test_ok_ () { 195 | test_count=$(expr "$test_count" + 1) 196 | say_color "" " ok $test_count: $@" 197 | } 198 | 199 | test_failure_ () { 200 | test_count=$(expr "$test_count" + 1) 201 | test_failure=$(expr "$test_failure" + 1); 202 | say_color error "FAIL $test_count: $1" 203 | shift 204 | echo "$@" | sed -e 's/^/ /' 205 | test "$immediate" = "" || { trap - exit; exit 1; } 206 | } 207 | 208 | test_known_broken_ok_ () { 209 | test_count=$(expr "$test_count" + 1) 210 | test_fixed=$(($test_fixed+1)) 211 | say_color "" " FIXED $test_count: $@" 212 | } 213 | 214 | test_known_broken_failure_ () { 215 | test_count=$(expr "$test_count" + 1) 216 | test_broken=$(($test_broken+1)) 217 | say_color skip " still broken $test_count: $@" 218 | } 219 | 220 | test_debug () { 221 | test "$debug" = "" || eval "$1" 222 | } 223 | 224 | test_run_ () { 225 | eval >&3 2>&4 "$1" 226 | eval_ret="$?" 227 | return 0 228 | } 229 | 230 | test_skip () { 231 | this_test=$(expr "./$0" : '.*/\(t[0-9]*\)-[^/]*$') 232 | this_test="$this_test.$(expr "$test_count" + 1)" 233 | to_skip= 234 | for skp in $GIT_SKIP_TESTS 235 | do 236 | case "$this_test" in 237 | $skp) 238 | to_skip=t 239 | esac 240 | done 241 | case "$to_skip" in 242 | t) 243 | say_color skip >&3 "skipping test: $@" 244 | test_count=$(expr "$test_count" + 1) 245 | say_color skip "skip $test_count: $1" 246 | : true 247 | ;; 248 | *) 249 | false 250 | ;; 251 | esac 252 | } 253 | 254 | test_expect_failure () { 255 | test "$#" = 2 || 256 | error "bug in the test script: not 2 parameters to test-expect-failure" 257 | if ! test_skip "$@" 258 | then 259 | say >&3 "checking known breakage: $2" 260 | test_run_ "$2" 261 | if [ "$?" = 0 -a "$eval_ret" = 0 ] 262 | then 263 | test_known_broken_ok_ "$1" 264 | else 265 | test_known_broken_failure_ "$1" 266 | fi 267 | fi 268 | echo >&3 "" 269 | } 270 | 271 | test_expect_success () { 272 | test "$#" = 2 || 273 | error "bug in the test script: not 2 parameters to test-expect-success" 274 | test_name="$1" 275 | if ! test_skip "$@" 276 | then 277 | say >&3 "expecting success: $2" 278 | test_run_ "$2" 279 | if [ "$?" = 0 -a "$eval_ret" = 0 ] 280 | then 281 | test_ok_ "$1" 282 | else 283 | test_failure_ "$@" 284 | fi 285 | fi 286 | echo >&3 "" 287 | } 288 | 289 | test_expect_code () { 290 | test "$#" = 3 || 291 | error "bug in the test script: not 3 parameters to test-expect-code" 292 | if ! test_skip "$@" 293 | then 294 | say >&3 "expecting exit code $1: $3" 295 | test_run_ "$3" 296 | if [ "$?" = 0 -a "$eval_ret" = "$1" ] 297 | then 298 | test_ok_ "$2" 299 | else 300 | test_failure_ "$@" 301 | fi 302 | fi 303 | echo >&3 "" 304 | } 305 | 306 | # This is not among top-level (test_expect_success | test_expect_failure) 307 | # but is a prefix that can be used in the test script, like: 308 | # 309 | # test_expect_success 'complain and die' ' 310 | # do something && 311 | # do something else && 312 | # test_must_fail git checkout ../outerspace 313 | # ' 314 | # 315 | # Writing this as "! git checkout ../outerspace" is wrong, because 316 | # the failure could be due to a segv. We want a controlled failure. 317 | 318 | test_must_fail () { 319 | "$@" 320 | test $? -gt 0 -a $? -le 129 321 | } 322 | 323 | # test_cmp is a helper function to compare actual and expected output. 324 | # You can use it like: 325 | # 326 | # test_expect_success 'foo works' ' 327 | # echo expected >expected && 328 | # foo >actual && 329 | # test_cmp expected actual 330 | # ' 331 | # 332 | # This could be written as either "cmp" or "diff -u", but: 333 | # - cmp's output is not nearly as easy to read as diff -u 334 | # - not all diff versions understand "-u" 335 | 336 | test_cmp() { 337 | echo "t=${GIT_TEST_CMP}" 338 | $GIT_TEST_CMP "$@" 339 | } 340 | 341 | # interpolate takes the contents of one file and interpolates the 342 | # given variables into it. E.g.: 343 | # 344 | # interpolate sourceFile destinationFile variableNameOne variableNameTwo 345 | # 346 | interpolate () { 347 | input_file=$1 348 | output_file=$2 349 | shift && shift 350 | data=$(cat $input_file) 351 | # Interpolate the renaming arguments 352 | for name in $* ; do 353 | eval value="$"$name 354 | data="${data//\$$name/$value}" 355 | done 356 | echo "$data" > $output_file 357 | } 358 | 359 | # Most tests can use the created repository, but some may need to create more. 360 | # Usage: test_create_repo 361 | test_create_repo () { 362 | test "$#" = 1 || 363 | error "bug in the test script: not 1 parameter to test-create-repo" 364 | owd=`pwd` 365 | repo="$1" 366 | mkdir "$repo" 367 | cd "$repo" || error "Cannot setup test environment" 368 | "git" init >/dev/null 2>&1 || 369 | error "cannot run git init -- have you built things yet?" 370 | mv .git/hooks .git/hooks-disabled 371 | cd "$owd" 372 | } 373 | 374 | test_done () { 375 | trap - exit 376 | 377 | if test "$test_fixed" != 0 378 | then 379 | say_color pass "fixed $test_fixed known breakage(s)" 380 | fi 381 | if test "$test_broken" != 0 382 | then 383 | say_color error "still have $test_broken known breakage(s)" 384 | msg="remaining $(($test_count-$test_broken)) test(s)" 385 | else 386 | msg="$test_count test(s)" 387 | fi 388 | case "$test_failure" in 389 | 0) 390 | # We could: 391 | # cd .. && rm -fr 'trash directory' 392 | # but that means we forbid any tests that use their own 393 | # subdirectory from calling test_done without coming back 394 | # to where they started from. 395 | # The Makefile provided will clean this test area so 396 | # we will leave things as they are. 397 | 398 | say_color pass "passed all $msg" 399 | exit 0 ;; 400 | 401 | *) 402 | say_color error "failed $test_failure among $msg" 403 | exit 1 ;; 404 | 405 | esac 406 | } 407 | 408 | # Test the binaries we have just built. The tests are kept in 409 | # t/ subdirectory and are run in 'trash directory' subdirectory. 410 | PATH=$(pwd)/..:$PATH 411 | unset GIT_CONFIG 412 | unset GIT_CONFIG_LOCAL 413 | GIT_CONFIG_NOSYSTEM=1 414 | GIT_CONFIG_NOGLOBAL=1 415 | export PATH GIT_CONFIG_NOSYSTEM GIT_CONFIG_NOGLOBAL 416 | 417 | # Test repository 418 | test="trash directory" 419 | rm -fr "$test" || { 420 | trap - exit 421 | echo >&5 "FATAL: Cannot prepare test area" 422 | exit 1 423 | } 424 | 425 | test_create_repo "$test" 426 | # Use -P to resolve symlinks in our working directory so that the cwd 427 | # in subprocesses like git equals our $PWD (for pathname comparisons). 428 | cd -P "$test" || exit 1 429 | 430 | this_test=$(expr "./$0" : '.*/\(t[0-9]*\)-[^/]*$') 431 | for skp in $GIT_SKIP_TESTS 432 | do 433 | to_skip= 434 | for skp in $GIT_SKIP_TESTS 435 | do 436 | case "$this_test" in 437 | $skp) 438 | to_skip=t 439 | esac 440 | done 441 | case "$to_skip" in 442 | t) 443 | say_color skip >&3 "skipping test $this_test altogether" 444 | say_color skip "skip all tests in $this_test" 445 | test_done 446 | esac 447 | done 448 | 449 | TRASH_HOOKS="$(git rev-parse --git-dir)/hooks" 450 | mkdir -p "$TRASH_HOOKS" 451 | 452 | install_client_hook () { 453 | cp ../../client/$1 "$TRASH_HOOKS/$2" 454 | chmod +x "$TRASH_HOOKS/$2" 455 | } 456 | 457 | install_post_checkout_hook () { 458 | mkdir -p ".git/hooks" 459 | hook=".git/hooks/post-checkout" 460 | 461 | echo "#!/bin/sh" >$hook 462 | for ((i=1;i<=$#;i+=1)); do 463 | eval script_name="$"$i 464 | echo "../../client/$script_name \$1 \$2 \$3 &&" >>$hook 465 | done 466 | echo "echo >/dev/null" >>$hook 467 | 468 | chmod +x $hook 469 | } 470 | 471 | install_server_hook () { 472 | mkdir -p "server.git/hooks" 473 | cp "../../server.git/$1" "server.git/hooks/$2" 474 | chmod +x "server.git/hooks/$2" 475 | } 476 | 477 | install_update_hook () { 478 | mkdir -p "server.git/hooks" 479 | hook="server.git/hooks/update" 480 | 481 | echo "#!/bin/bash" >$hook 482 | for ((i=1;i<=$#;i+=1)); do 483 | eval script_name="$"$i 484 | echo "../../../server/$script_name \$1 \$2 \$3 &&" >>$hook 485 | done 486 | echo "echo >/dev/null" >>$hook 487 | 488 | chmod +x $hook 489 | } 490 | 491 | install_post_receive_hook () { 492 | mkdir -p "server.git/hooks" 493 | hook="server.git/hooks/post-receive" 494 | 495 | cat >$hook <<-'EOF' 496 | #!/bin/bash 497 | nl=$'\n' 498 | input="" 499 | while read newref oldref refname ; do 500 | input="$input$newref $oldref $refname$nl" 501 | done 502 | EOF 503 | 504 | for ((i=1;i<=$#;i+=1)); do 505 | eval script_name="$"$i 506 | echo "echo -n \"\$input\" | ../../../server/$script_name" >>$hook 507 | done 508 | 509 | chmod +x $hook 510 | } 511 | 512 | -------------------------------------------------------------------------------- /workflow.markdown: -------------------------------------------------------------------------------- 1 | 2 | Overview 3 | ======== 4 | 5 | I've found this workflow works well for close-collaboration topic branches in a typical enterprise software development project. 6 | 7 | By close-collaboration, I mean teams that are frequently collaborating on topic branches via a shared server and so do not to rebase their topic branches all the time. 8 | 9 | Briefly: 10 | 11 | * stable replaces master as the "special" branch that is always at the latest released version 12 | * topic branches are created off of stable and worked on, merging in results from stable as new releases come out 13 | * candidate branches are created as a roll up of several topic branches in anticipation for a release--qa works on the candidate branches and the candidate branch is merged into stable when released 14 | 15 | stable 16 | ====== 17 | 18 | We dropped the master branch and started using "stable" as the branch that represented the latest release. When new a release came out and stable moved, each topic branch would `git merge origin/stable` as needed. 19 | 20 | To keep the DAG clean, we wanted the first-parent of each commit on stable to be the previous release. 21 | 22 | This is different than how we first managed stable's DAG, as we'd let stable be fast-forwarded to whatever qa just got done certifying. E.g. it would look like: 23 | 24 | A stable 25 | |\ 26 | | * -- * -- topic1 27 | | \ 28 | | * -- B candidate_1.1 29 | \ / 30 | -- * -- topic2 31 | 32 | And when candidate_1.1 was released, we'd checkout stable, `git merge candidate_1.1` and get a fast-forward so that stable was now at commit B. Which made sense, and worked, but it meant to track the 1.0 -> 1.1 change, you'd have to dig through the topic branch mess between A and B. 33 | 34 | So we moved to: 35 | 36 | A --------------- C stable 37 | |\ | 38 | | * -- * -- | topic1 39 | | \ / 40 | | * -- B candidate_1.1 41 | \ / 42 | -- * -- topic2 43 | 44 | When candidate_1.1 is released, we checkout stable, `git merge --no-ff candidate_1.1` and force a new merge commit. This means commit A (1.0) is a direct first parent of commit C (1.1) and makes the DAG much nicer to follow. 45 | 46 | Note that the [update-stable][1] hook enforces this first-parent movement of stable and the [update-ensure-follows][2] enforces topic branches merge in the new release at their earliest possible convenience (i.e. before being able to push again). 47 | 48 | [1]: server/update-stable 49 | [2]: server/update-ensure-follows 50 | 51 | candidates 52 | ========== 53 | 54 | Candidate branches are just gatherings of topic branches that have been deemed releasable by development and qa and so are ready for integration testing. 55 | 56 | The only trick here is judicious use of `--no-ff` again as otherwise the first topic branch you merge into your new candidate branch will likely result in your candidate branch being fast-forwarded to where ever the topic branch is at. 57 | 58 | topics 59 | ====== 60 | 61 | Topics are fairly obvious. 62 | 63 | rebase vs. merge 64 | ================ 65 | 66 | Merging: 67 | 68 | * Is great for cross-branch scenarios--merging candidates into stable, stable into topics, etc. 69 | * In cross-branch scenarios, fast-forwarding generally isn't preferred as seeing a no-op merge commit into stable or into a new candidate is still useful for DAG aesthetics 70 | 71 | Rebasing: 72 | 73 | * Is great for local commits to avoid same-branch merges (where a dev has a local, unshared commit and then creates a useless merge of it into the same branch that has since moved on) 74 | * Is a PITA unless you use the [pull][3] scripts 75 | 76 | [3]: scripts/pull 77 | 78 | --------------------------------------------------------------------------------