| 1 | #!/bin/bash |
| 2 | set -e |
| 3 | |
| 4 | # stg-cvs - helper script to manage a mixed cvs/stgit working copy. |
| 5 | |
| 6 | # Allows quick synchronization of a cvs mirror branch (does not try to |
| 7 | # reconstruct patchsets, creates "jumbo" commits), and commits stgit |
| 8 | # patches to CVS. |
| 9 | |
| 10 | # Copyright (c) 2007 Yann Dirson <ydirson@altern.org> |
| 11 | # Subject to the GNU GPL, version 2. |
| 12 | |
| 13 | # NOTES |
| 14 | # - you want to add a "CVS" line to .git/info/exclude |
| 15 | # - you may want to add a ".git" line to the top .cvsignore |
| 16 | |
| 17 | # BRANCH INIT |
| 18 | # - ensure the cvs wc is clean (eg. with "cvsco") |
| 19 | # $ git init |
| 20 | # $ echo CVS >> .git/info/exclude |
| 21 | # $ git add . |
| 22 | # $ git commit -m "Initial import." |
| 23 | # $ git branch -m master cvs |
| 24 | # $ stg branch -c master cvs |
| 25 | # $ git config branch.master.stgit.parentbranch cvs (0.12.1 and earlier only) |
| 26 | # $ git config branch.cvs.description "CVS $(cat CVS/Root) $(cat CVS/Repository) $(cat CVS/Tag 2>/dev/null | echo HEAD)" |
| 27 | # $ git config branch.master.description "Changes for $(cat CVS/Repository) $(cat CVS/Tag 2>/dev/null | echo HEAD)" |
| 28 | |
| 29 | # LIMITATIONS |
| 30 | # - this is only a proof-of-concept prototype |
| 31 | # - lacks an "init" command (see above) |
| 32 | # - "commit" does not ensure the base is uptodate before trying to |
| 33 | # commit (but hey, it's CVS ;): better "stg-cvs pull" first |
| 34 | # - "commit" can only commit a single patch |
| 35 | # - not much robustness here |
| 36 | # - still no support for files removed in cvs (should catch "no |
| 37 | # longer in the repository" message) |
| 38 | # - this only deals with CVS but could surely be extended to any other |
| 39 | # VCS |
| 40 | # - lacks synchronisation of .cvsignore <-> .gitignore |
| 41 | # - no support for filenames with spaces (stg lacks --zero output format) |
| 42 | # - git-commit is too chatty when it finds nothing to commit |
| 43 | # - lacks a "quick cvs commit" feature |
| 44 | |
| 45 | # DESIGN FLAWS |
| 46 | # - while fetching, if a file change was not git-update-index'd when |
| 47 | # cvs-update'd (eg. because of a stg-cvs bug), it is not seen on further |
| 48 | # fetches until it changes again, since we scan "cvs update" output. |
| 49 | # This yields possible inconsistencies with CVS. |
| 50 | # - similarly, any conflict while cvs-updating (whether due to illegal |
| 51 | # changes to the cvs-mirror-branch, or due to files added to cvs but |
| 52 | # already-existing in working copy, or to directory moves inside the |
| 53 | # cvs repository, or <fill here>) has to be dealt with by hand (although |
| 54 | # the situation is better here: cvs sees the conflict on subsequent tries) |
| 55 | # - bad/no support for cvsutils: |
| 56 | # - stg push/pop operations confuse cvsu because of timestamp changes |
| 57 | # - cvspurge/cvsco would nuke .git => does not make it easy to ensure |
| 58 | # synchronisation |
| 59 | # - should use a separate workspace for cvs branch like tailor does |
| 60 | # - confused by cvs keyword substitution |
| 61 | |
| 62 | usage() |
| 63 | { |
| 64 | [ "$#" = 0 ] || echo "ERROR: $*" |
| 65 | echo "Usage: $(basename $0) <command>" |
| 66 | echo " commands: $(do_commands)" |
| 67 | exit 1 |
| 68 | } |
| 69 | |
| 70 | do_commands() |
| 71 | { |
| 72 | echo $(grep '^[a-z-]*)' $0 | cut -d')' -f1) |
| 73 | } |
| 74 | |
| 75 | do_fetch() |
| 76 | { |
| 77 | local return=0 |
| 78 | local path |
| 79 | |
| 80 | local parent="$1" |
| 81 | local branch="$2" |
| 82 | |
| 83 | # record changes from cvs into index |
| 84 | stg branch "$parent" || exit $? |
| 85 | cvs -fq update -dP | grep -v '^\? ' | tee /dev/tty | while read status path; do |
| 86 | if [ -e "$path" ]; then |
| 87 | git update-index --add "$path" || exit $? |
| 88 | else |
| 89 | git update-index --remove "$path" || exit $? |
| 90 | fi |
| 91 | # cvs update: `FELIN1_PEP/src/java/com/sagem/felin/ui/widget/PEPRifStateIcon.java' is no longer in the repository |
| 92 | done |
| 93 | |
| 94 | # create commit |
| 95 | if git commit -m "stg-cvs sync"; then |
| 96 | : |
| 97 | else |
| 98 | return=$? |
| 99 | fi |
| 100 | |
| 101 | # back to branch |
| 102 | stg branch "$branch" || exit $? |
| 103 | |
| 104 | return $return |
| 105 | } |
| 106 | |
| 107 | cvs_add_dir() |
| 108 | { |
| 109 | local parent=$(dirname "$1") |
| 110 | if [ ! -e "$parent/CVS" ]; then |
| 111 | cvs_add_dir "$parent" |
| 112 | fi |
| 113 | |
| 114 | cvs add "$1" |
| 115 | } |
| 116 | |
| 117 | # get context |
| 118 | branch=$(stg branch) |
| 119 | parent=$(git-config "branch.${branch}.stgit.parentbranch") || |
| 120 | usage "no declared parent for '$branch' - set branch.${branch}.stgit.parentbranch" |
| 121 | |
| 122 | # extract command |
| 123 | |
| 124 | [ "$#" -ge 1 ] || usage |
| 125 | command="$1" |
| 126 | shift |
| 127 | |
| 128 | case "$command" in |
| 129 | fetch) |
| 130 | do_fetch "$parent" "$branch" |
| 131 | ;; |
| 132 | |
| 133 | pull) |
| 134 | if do_fetch "$parent" "$branch"; then |
| 135 | # update |
| 136 | # --merged |
| 137 | stg rebase "$parent" |
| 138 | stg clean --applied |
| 139 | fi |
| 140 | ;; |
| 141 | |
| 142 | commit) |
| 143 | # sanity asserts |
| 144 | [ $(stg applied | wc -l) = 1 ] || |
| 145 | usage "you don't have exactly one patch applied" |
| 146 | |
| 147 | # context |
| 148 | patch=$(stg top) |
| 149 | |
| 150 | # adds |
| 151 | stg files | grep ^A | cut -c3- | while read file; do |
| 152 | parent=$(dirname "$file") |
| 153 | if [ ! -e "$parent/CVS" ]; then |
| 154 | cvs_add_dir "$parent" |
| 155 | fi |
| 156 | cvs -f add "$file" |
| 157 | done |
| 158 | |
| 159 | # removes |
| 160 | stg files | grep ^D | cut -c3- | xargs -r cvs -f remove |
| 161 | |
| 162 | # commit |
| 163 | stg files --bare | xargs -r cvs -fq commit \ |
| 164 | -F ".git/patches/$branch/patches/$patch/description" |
| 165 | |
| 166 | # sync the parent branch |
| 167 | stg branch "$parent" |
| 168 | git-cherry-pick "patches/${branch}/${patch}" |
| 169 | stg branch "${branch}" |
| 170 | |
| 171 | # update |
| 172 | # --merged |
| 173 | stg rebase "$parent" |
| 174 | stg clean --applied |
| 175 | ;; |
| 176 | |
| 177 | _commands) |
| 178 | # hint for bash-completion people :) |
| 179 | do_commands |
| 180 | ;; |
| 181 | |
| 182 | *) |
| 183 | usage "unknown command '$command'" |
| 184 | ;; |
| 185 | esac |