git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* RFC: Between git-subtree and git-submodules
@ 2010-07-24  0:13 Santi Béjar
  2010-07-24  0:57 ` Avery Pennarun
  0 siblings, 1 reply; 5+ messages in thread
From: Santi Béjar @ 2010-07-24  0:13 UTC (permalink / raw)
  To: Git Mailing List, Avery Pennarun

[-- Attachment #1: Type: text/plain, Size: 4502 bytes --]

Hi *,

  now that there is a lot of traffic about git-subtree and git-submodules I
  would like to show you why *I* don't use neither of them, but something in
  between.

  First my requirements:

1) Everything[1] must be available from the same repository/branch (so I'm not
   worried about repository size)
2) The history must be as clean as possible
3) The directory content must be equal to the external module, at least when
   you add/update it[2]
4) The external module should be able to switch back and forth between
   different versions.

[1] Everything means all that you need to checkout all the commits in the
superproject not in the submodule.
[2] A consequence of 3) is that I lose all
change I've made in the subdirectory, if they are important I have to extract
them, apply them and add the module back.

git-submodule is rule out because of 1) but accomplish 2), 3) and
4). git-subtree is rule out because of 2) (even with --squash) 3) and 4)
without --squash but accomplish 1) and 4) with --squash. So I need something
in between or a mixture of both.

At the end I've done what I called originally git-subtree, but now I've written
the prune mode of git-subtree.

The idea is that you want to add the content of a module in a subdirectory and
that's all! I think that this simplicity is also very powerful as it
is very clear how it
behaves. You just get a commit each time you add a subtree (normal commit
not a merge) without the history of the subtree.

You get something like this:

$ git log --graph
* ee225bd Subtree 'a/': 895916a Commit message 1
* aa345dg Modification to a/Makefile
* ddcd676 Subtree 'a/': 9a053f2 Commit message 2
* ea35faf Indent, whitespaces,...

Later you can extract/split all the modifications to a/ with "git-subtree
split" (not yet implemented).

If you merge two branches with different content in the subtree you just merge
them the normal way as with any other file.

It works quite well in my use case.

Below you can find the patch to git-subtree to add this prune mode (also
attatched because of whitespace corruption). Only slightly tested as I
normally use
my git-subtree command (completely different) and I've just patch git-subtree
but you can get the idea of what it does.

HTH,
Santi

diff --git i/git-subtree.sh w/git-subtree.sh
index 781eef3..7706d72 100755
--- i/git-subtree.sh
+++ w/git-subtree.sh
@@ -27,6 +27,7 @@ onto=         try connecting new tree to an existing one
 rejoin        merge the new branch back into HEAD
  options for 'add', 'merge', 'pull' and 'push'
 squash        merge subtree changes as a single commit
+prune         prune history
 "
 eval $(echo "$OPTS_SPEC" | git rev-parse --parseopt -- "$@" || echo exit $?)

@@ -44,6 +45,7 @@ rejoin=
 ignore_joins=
 annotate=
 squash=
+prune=
 message=

 debug()
@@ -92,6 +94,8 @@ while [ $# -gt 0 ]; do
 		--no-ignore-joins) ignore_joins= ;;
 		--squash) squash=1 ;;
 		--no-squash) squash= ;;
+		--prune) prune=1;;
+		--no-prune) prune=;;
 		--) break ;;
 		*) die "Unexpected option: $opt" ;;
 	esac
@@ -110,7 +114,7 @@ if [ -z "$prefix" ]; then
 fi

 case "$command" in
-	add) [ -e "$prefix" ] &&
+	add) [ -e "$prefix" -a -z "$prune" ] &&
 		die "prefix '$prefix' already exists." ;;
 	*)   [ -e "$prefix" ] ||
 		die "'$prefix' does not exist; use 'git subtree add'" ;;
@@ -359,6 +363,17 @@ squash_msg()
 	echo "git-subtree-split: $newsub"
 }

+prune_msg()
+{
+	dir="$1"
+	newsub="$2"
+	
+	git show -s --pretty="tformat:Subtree '$dir/': %h %s" $newsub
+	echo
+	echo "git-subtree-dir: $dir"
+	echo "git-subtree-split: $newsub"
+}
+
 toptree_for_commit()
 {
 	commit="$1"
@@ -464,7 +479,7 @@ ensure_clean()

 cmd_add()
 {
-	if [ -e "$dir" ]; then
+	if [ -e "$dir" -a -z "$prune" ]; then
 		die "'$dir' already exists.  Cannot add."
 	fi

@@ -498,6 +513,10 @@ cmd_add_commit()
 	rev="$1"
 	
 	debug "Adding $dir as '$rev'..."
+	if [ -d "$dir" ]; then
+	    #TODO: write it with plumbing commands
+	    git rm -r -q $dir
+	fi
 	git read-tree --prefix="$dir" $rev || exit $?
 	git checkout -- "$dir" || exit $?
 	tree=$(git write-tree) || exit $?
@@ -513,6 +532,9 @@ cmd_add_commit()
 		rev=$(new_squash_commit "" "" "$rev") || exit $?
 		commit=$(add_squashed_msg "$rev" "$dir" |
 			 git commit-tree $tree $headp -p "$rev") || exit $?
+	elif [ -n "$prune" ]; then
+		commit=$(prune_msg "$dir" "$rev" |
+			 git commit-tree $tree -p $headrev) || exit $?
 	else
 		commit=$(add_msg "$dir" "$headrev" "$rev" |
 			 git commit-tree $tree $headp -p "$rev") || exit $?

[-- Attachment #2: git-subtree-prune-mode.patch --]
[-- Type: text/x-diff, Size: 2257 bytes --]

diff --git i/git-subtree.sh w/git-subtree.sh
index 781eef3..7706d72 100755
--- i/git-subtree.sh
+++ w/git-subtree.sh
@@ -27,6 +27,7 @@ onto=         try connecting new tree to an existing one
 rejoin        merge the new branch back into HEAD
  options for 'add', 'merge', 'pull' and 'push'
 squash        merge subtree changes as a single commit
+prune         prune history
 "
 eval $(echo "$OPTS_SPEC" | git rev-parse --parseopt -- "$@" || echo exit $?)
 
@@ -44,6 +45,7 @@ rejoin=
 ignore_joins=
 annotate=
 squash=
+prune=
 message=
 
 debug()
@@ -92,6 +94,8 @@ while [ $# -gt 0 ]; do
 		--no-ignore-joins) ignore_joins= ;;
 		--squash) squash=1 ;;
 		--no-squash) squash= ;;
+		--prune) prune=1;;
+		--no-prune) prune=;;
 		--) break ;;
 		*) die "Unexpected option: $opt" ;;
 	esac
@@ -110,7 +114,7 @@ if [ -z "$prefix" ]; then
 fi
 
 case "$command" in
-	add) [ -e "$prefix" ] && 
+	add) [ -e "$prefix" -a -z "$prune" ] &&
 		die "prefix '$prefix' already exists." ;;
 	*)   [ -e "$prefix" ] || 
 		die "'$prefix' does not exist; use 'git subtree add'" ;;
@@ -359,6 +363,17 @@ squash_msg()
 	echo "git-subtree-split: $newsub"
 }
 
+prune_msg()
+{
+	dir="$1"
+	newsub="$2"
+	
+	git show -s --pretty="tformat:Subtree '$dir/': %h %s" $newsub
+	echo
+	echo "git-subtree-dir: $dir"
+	echo "git-subtree-split: $newsub"
+}
+
 toptree_for_commit()
 {
 	commit="$1"
@@ -464,7 +479,7 @@ ensure_clean()
 
 cmd_add()
 {
-	if [ -e "$dir" ]; then
+	if [ -e "$dir" -a -z "$prune" ]; then
 		die "'$dir' already exists.  Cannot add."
 	fi
 
@@ -498,6 +513,10 @@ cmd_add_commit()
 	rev="$1"
 	
 	debug "Adding $dir as '$rev'..."
+	if [ -d "$dir" ]; then
+	    #TODO: write it with plumbing commands
+	    git rm -r -q $dir
+	fi
 	git read-tree --prefix="$dir" $rev || exit $?
 	git checkout -- "$dir" || exit $?
 	tree=$(git write-tree) || exit $?
@@ -513,6 +532,9 @@ cmd_add_commit()
 		rev=$(new_squash_commit "" "" "$rev") || exit $?
 		commit=$(add_squashed_msg "$rev" "$dir" |
 			 git commit-tree $tree $headp -p "$rev") || exit $?
+	elif [ -n "$prune" ]; then
+		commit=$(prune_msg "$dir" "$rev" |
+			 git commit-tree $tree -p $headrev) || exit $?
 	else
 		commit=$(add_msg "$dir" "$headrev" "$rev" |
 			 git commit-tree $tree $headp -p "$rev") || exit $?

^ permalink raw reply related	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2010-07-26  5:03 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-07-24  0:13 RFC: Between git-subtree and git-submodules Santi Béjar
2010-07-24  0:57 ` Avery Pennarun
2010-07-24  8:33   ` Santi Béjar
2010-07-25  0:28   ` Santi Béjar
2010-07-26  5:02     ` Avery Pennarun

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).