Test commit
[cogito/jonas.git] / cg-push
blob30fe3f358aa8b828d2d9bb6aae910585b86339ef
1 #!/usr/bin/env bash
3 # Push changes to a remote repository
4 # Copyright (c) Petr Baudis, 2005.
6 # It will push your commits on the current branch (or as specified by
7 # the -r option) to one or more remote repositories, provided that your
8 # commits follow the last commit in each of the remote repositories.
10 # Note that if a remote repository is associated with a working
11 # tree copy, this command won't update that. Use cg-reset at the
12 # remote side to bring it in sync (but throw away any local changes
13 # in that tree). Consider setting up a standalone repository (see
14 # `cg-admin-setuprepo`).
16 # You can set up update hooks in the remote repository to bind
17 # any action to the push (e.g. sending an email or CIA notification
18 # or even verifying if the commits are well-formed before letting
19 # them in). See `git-receive-pack`(1) documentation for details.
21 # Takes the branch names as arguments, defaulting to 'origin' or the
22 # current branch's default remote branch, see `cg-fetch` for details.
24 # OPTIONS
25 # -------
26 # -f:: Force push even if the fast-forward check fails (DANGEROUS)
27 # Force the push even if the fast-forward check fails, that is,
28 # the commit you are pushing is not a descendant of the current
29 # commit in the remote branch. DO NOT USE THIS FLAG - that error
30 # usually means that someone else pushed out some commits in
31 # the meantime and you should do `cg-update` in order to get
32 # them merged locally, then try to push again.
34 # You might want to use this flag only if you had to rewrite your
35 # history (e.g. using `cg-admin-uncommit` or 'cg-commit --amend')
36 # and now you want to push the new history out. However if you
37 # published your original commits in the meantime, you are now
38 # setting up quite some trouble for others who track your repository
39 # since Git will get confused. Use with care.
41 # -r BRANCH:: Push the given branch
42 # Pushes the given branch instead of the current one. Note that
43 # we lie a little here and you can actually specify a particular
44 # commit here, but you probably will not want to do that.
46 # -t TAG:: Push the given TAG
47 # Tells cg-push to also push the given tag. Note that in the
48 # future, cg-push should push tags automatically. Also note
49 # that even if you pass `cg-push` the '-t' arguments, your
50 # HEAD is still pushed as well in addition to the tags.
52 # Testsuite: TODO
54 USAGE="cg-push [-f] [-r LOCAL_BRANCH] [-t TAG]... [REMOTE_BRANCH]..."
55 _git_wc_unneeded=1
57 . "${COGITO_LIB}"cg-Xlib || exit 1
60 send_pack_update()
62 name="$1"; shift
63 commit="$(cg-object-id -c "$locbranch")"
64 old="$(get_ref "refs/heads/$name")" || : may stay empty
65 git-send-pack $force "$@" && git-update-ref refs/heads/"$name" "$commit" $old
69 locbranch="$_git_head"
70 tags=()
71 force=
72 while optparse; do
73 if optparse -r=; then
74 locbranch="$OPTARG"
75 [ "$(cg-object-id -c "$locbranch")" ] || exit 1
76 elif optparse -t=; then
77 tags[${#tags[@]}]="refs/tags/$OPTARG"
78 elif optparse -f; then
79 force="--force"
80 else
81 optfail
83 done
85 push_branch()
87 name="$1"
88 uri="$(cat "$_git/branches/$name" 2>/dev/null)" || die "unknown branch: $name"
90 rembranch=master
91 if echo "$uri" | grep -q '#'; then
92 rembranch="$(echo "$uri" | cut -d '#' -f 2)"
93 uri="$(echo "$uri" | cut -d '#' -f 1)"
95 sprembranch=":refs/heads/$rembranch"
97 if [ "${uri#http://}" != "$uri" -o "${uri#https://}" != "$uri" ]; then
98 git-http-push $force "$uri/" "$locbranch$sprembranch" "${tags[@]}"
100 elif [ "${uri#git+ssh://}" != "$uri" ]; then
101 send_pack_update "$name" "$(echo "$uri" | sed 's#^git+ssh://\([^/]*\)\(/.*\)$#\1:\2#')" "$locbranch$sprembranch" "${tags[@]}"
103 elif [ "${uri#rsync://}" != "$uri" ]; then
104 die "pushing over rsync not supported"
106 elif [ "${uri#*:}" != "$uri" ]; then
107 echo "WARNING: I guessed the host:path syntax was used and fell back to the git+ssh protocol."
108 echo "WARNING: The host:path syntax is evil because it is implicit. Please just use a URI."
109 send_pack_update "$name" "$uri" "$locbranch$sprembranch" "${tags[@]}"
111 else
112 remgit="$uri"; [ -d "$remgit/.git" ] && remgit="$remgit/.git"
113 if is_same_repo "$_git_objects" "$remgit/objects"; then
114 commit="$(cg-object-id -c "$locbranch")"
115 remid="$(GIT_DIR="$remgit" get_ref refs/heads/$rembranch)" || die "$remgit: no branch $master"
116 if [ "$remid" = "$commit" ] && [ ! "${tags[*]}" ]; then
117 echo "$remgit#$rembranch: Already up-to-date." >&2
118 exit 0
120 if [ "$remid" != "$(git-merge-base "$remid" "$commit")" ]; then
121 if [ -z "$force" ]; then
122 echo "ERROR: Remote '$rembranch' has some changes you don't have in your '$locbranch'" >&2
123 echo "Try to cg-update from it first, then push." >&2
124 exit 1
125 else
126 echo "Warning: Forcing $rembranch update even though remote $remid is not ancestor of local $commit." >&2
130 echo "Pushing $commit to $remgit#$rembranch" >&2
131 [ -x "$remgit/hooks/update" ] && "$remgit/hooks/update" "$rembranch" "$remid" "$commit"
132 GIT_DIR="$remgit" git-update-ref refs/heads/"$rembranch" "$commit" "$remid" || die "push failed"
133 git-update-ref refs/heads/"$name" "$commit"
134 for tag in "${tags[@]}"; do
135 tagval="$(get_ref "refs/tags/$tag")"
136 GIT_DIR="$remgit" git-update-ref refs/tags/"$tag" "$tagval"
137 done
138 [ -x "$remgit/hooks/post-update" ] && "$remgit/hooks/post-update" "$rembranch"
139 else
140 send_pack_update "$name" "$uri" "$locbranch$sprembranch" "${tags[@]}"
145 if [ "${#ARGS[@]}" == 0 ]; then
146 name="$(choose_origin branches "where to push to?")" || exit 1
147 push_branch "$name"
149 else
150 for name in "${ARGS[@]}"; do
151 push_branch "$name"
152 done