jerous' ::1

2016-07-26 [E] [D]
code,programming Comments 0

On this server I host several git repositories, and sometimes I want to add update a single file in a large repository. It would be quite convenient if I could just update the bare repository without doing a checkout (it saves on space, no trouble with outdated repositories etc)

Obviously, someone on the internet already had asked the same question. I just put it in a nice script, with a simple test to ensure it works :)

You can download the script called git-bare-add, and view the source of the script below.

Usage is quite simple: git bare-add [-b $BRANCH] $BARE-REPO $REL-PATH $NEW-ABS-FILE $COMMIT-MSG

Here, $BARE-REPO is the path to the bare repository, $REL-PATH the path to the file inside the repository that should be updated. $NEW-ABS-FILE is the absolute filepath to the file that should be copied to $REL-PATH inside the repository. $COMMIT-MSG is (obviously) the commit message.

One can also edit a file in place, by using the option --edit and dropping the $NEW-ABS-FILE parameter: git bare-add --edit [-b $BRANCH] $BARE-REPO $REL-PATH $COMMIT-MSG

To perform some tests: git bare-add --test

It should be relatively safe to use this script, since it makes use of git porcelain stuff.

Note that there is no safety regarding multiple simultaneous accesses. Thus it is best used on a repository where you are for sure the only writer (during the whole length of the edit), since I think it will ignore commits made while edits are being made.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
#!/bin/sh

GIT=/usr/local/bin/git
CUT=/usr/bin/cut
MKTEMP=/usr/bin/mktemp
RM=/bin/rm
CP=/bin/cp
CAT=/bin/cat
PRINTF=/usr/bin/printf
SELF="$PWD/$0"

help() {
	echo "$0 [-b \$BRANCH] \$BARE-REPO \$REL-PATH \$NEW-ABS-FILE \$COMMIT-MSG"
	echo "    Adds/updates the file \$REL-PATH in a bare repository \$BARE-REPO"
	echo "    with the file \$NEW-ABS-FILE."
	echo
	echo "$0 --edit [-b \$BRANCH] \$BARE-REPO \$REL-PATH \$COMMIT-MSG"
	echo "    Creates a temporary file \$TMP with the contents of \$BARE-REPO/\$REL-PATH, and calls"
	echo "    $0 -b \$BRANCH \$BARE-REPO \$REL-PATH \$TMP \$COMMIT-MSG"
	echo
	echo "$0 --test"
	echo "    Runs a very limited set of tests"
	exit 1
}

die() {
	echo "$@" >&2
	exit 2
}

test-it() {
	cd /tmp
	REPO=$($MKTEMP)
	$RM "$REPO"
	
	$GIT init --bare "$REPO.git" >/dev/null 2>&1
	$GIT clone "$REPO.git" >/dev/null 2>&1
	
	$PRINTF "FILE1" > "$REPO.git/file1"
	$PRINTF "FILE2" > "$REPO.git/file2"
	
	$CP "$REPO.git/file1" "$REPO/file1"
	cd "$REPO"; $GIT add file1; $GIT commit -m "initial commit" >/dev/null; $GIT push 1>/dev/null 2>&1; cd .. # necessary to create a branch master
	
	$SELF "$REPO.git" "file2" "$REPO.git/file2" "Add file2"
	cd "$REPO"; $GIT pull 1>/dev/null 2>&1; if [ "$($CAT file2)" != "FILE2" ]; then echo "Invalid contents for file2 after add :("; fi; cd ..
	
	$PRINTF "FILE2b" > "$REPO.git/file2"
	$SELF "$REPO.git" "file2" "$REPO.git/file2" "Update file2"
	cd "$REPO"; $GIT pull 1>/dev/null 2>&1; if [ "$($CAT file2)" != "FILE2b" ]; then echo "Invalid contents for file2 after update :("; fi; cd ..
	
	# TODO also test branches

	$RM -rf "$REPO" "$REPO.git"
	echo "Done! If you didn't see any output, all went fine :)"
}

add-file() {
	local repo="$1"
	local relFilePath="$2"
	local srcAbsPath="$3"
	local commitMsg="$4"
	local branch="$5"

	cd "$repo"
	
	$GIT rev-parse --git-dir > /dev/null 2>&1 || die "Path '$PWD' is not a git repository"
	[ $($GIT config --get core.bare) == true ] || die "Repository '$PWD' is not a bare repository"
	[ -f "$srcAbsPath" ] || die "Source file '$srcAbsPath' does not exist"

	# This code is based on http://stackoverflow.com/a/25556917

	local PARENT_COMMIT="$($GIT show-ref -s "$branch")"

	# Empty the index, this might not be necessary. Better leave it here.
	$GIT read-tree --empty

	# Load the current tree
	$GIT read-tree "$PARENT_COMMIT"

	# Create a blob object.
	local BLOB_ID=$($GIT hash-object -w "$srcAbsPath")

	# Now add it to the index.
	$GIT update-index --add --cacheinfo 100644 "$BLOB_ID" "$relFilePath"

	# Create a tree from the new index
	local TREE_ID=$($GIT write-tree)

	# Commit it.
	local NEW_COMMIT=$(echo "$commitMsg" | $GIT commit-tree "$TREE_ID" -p "$PARENT_COMMIT")

	# Update the branch
	$GIT update-ref "refs/heads/$branch" "$NEW_COMMIT" "$PARENT_COMMIT"
}

edit-file() {

}

branch="master"
OLD_PWD="$PWD"
action="add"
set -e

while true; do
	case "$1" in
		--test) test-it; shift; exit 0 ;;
		--edit) action=edit; shift ;;
		-b) branch="$2"; shift; shift ;;
		-*) echo "Invalid argument"; help ;;
		*) break
	esac
done

case "$action" in
	add)
		if [ $# -ne 4 ]; then echo "Needs exactly 4 arguments, $# given."; help; fi
		repo="$1"
		relFilePath="$2"
		if [ "$(echo "$3" | $CUT -c1)" != '/' ]; then srcAbsPath="$OLD_PWD/$3"; else srcAbsPath="$3"; fi
		commitMsg="$4"
		add-file "$repo" "$relFilePath" "$srcAbsPath" "$commitMsg" "$branch"
		;;
	edit)
		if [ $# -ne 3 ]; then echo "Needs exactly 3 arguments, $# given."; help; fi
		repo="$1"
		relFilePath="$2"
		commitMsg="$3"
		srcAbsPath=$(mktemp)
		$GIT --git-dir "$repo" show "$branch":"$relFilePath" > "$srcAbsPath"
		$EDITOR "$srcAbsPath"
		
		echo "Commit the file '$relFilePath' ('$srcAbsPath') to repository '$repo'? Type 'y' to commit, 'n' to abort"
		while read reply; do
			case "$reply" in
				y)
					add-file "$repo" "$relFilePath" "$srcAbsPath" "$commitMsg" "$branch";
					rm "$srcAbsPath"
					break ;;
				n) echo "Not commiting file '$srcAbsPath', but keeping it." ;;
				*) echo "Enter 'y' or 'n'" ;;
			esac
		done
		;;
	*) echo "INVALID???" ;;
esac
 

Add a comment

Name (required)
Email (optional, not shown)
Comment (max 1000 characters)

[printer friendly] [static version] [Post listing] [Page listing] [Tags: music tab jazz travel wdb europe code live programming youtube record ]