From: Simon Richter <hidden> Date: 2016-06-15 22:42:16
Hello,
one thing that I have been missing so far in all SCM systems apart from
CVS (and there it's just coincidence) is the ability to include a
project as part of a bigger project. Developing software for embedded
systems, I need that feature fairly often, for example the source tree
for a particular device almost always contains one or more Linux trees,
some binutils, gcc and gdb stuff and so on.
The changes necessary here would be fairly simple: "tree" objects would
point to a "commit" or a "tag" object when a subproject is used.
In the working directory, this would be represented by a .git directory
that contains a symref to the embedding project instead of the objects
directory. Head pointers are only required if you intend to push changes
upstream to the maintainer of the embedded project. Each subproject has
its own index.
Would such a feature make sense, and what behaviour would make the most
sense for the various operations (e.g. shall commits in the inner
project propagate to the outer?)?
Simon
From: Johannes Schindelin <hidden> Date: 2016-06-15 22:42:16
Hi,
On Wed, 11 Jan 2006, Simon Richter wrote:
one thing that I have been missing so far in all SCM systems apart from CVS
(and there it's just coincidence) is the ability to include a project as part
of a bigger project. Developing software for embedded systems, I need that
feature fairly often, for example the source tree for a particular device
almost always contains one or more Linux trees, some binutils, gcc and gdb
stuff and so on.
What I do: I call it a branch. While this might seem technically
incorrect, it is not.
And since the subprojects are really independent, you can connect them by
an octopus.
The changes necessary here would be fairly simple: "tree" objects would point
to a "commit" or a "tag" object when a subproject is used.
Sorry, we discussed similar things already. It is not necessary to change
the structure. Even more: it makes no sense. Why would you want to have
two or more commit messages for the same revision?
Remember: trees, commits and tags (objects in general) are immutable. You
may think that you just commit a new revision of the subproject, and it is
picked up by the overall project, but that is not the case!
In the working directory, this would be represented by a .git directory that
contains a symref to the embedding project instead of the objects directory.
Head pointers are only required if you intend to push changes upstream to the
maintainer of the embedded project. Each subproject has its own index.
You can do this like I said: use branches (and possibly a common
GIT_OBJECT_DIRECTORY to save on disk space).
Hth,
Dscho
From: Simon Richter <hidden> Date: 2016-06-15 22:42:16
Hello,
Johannes Schindelin wrote:
And since the subprojects are really independent, you can connect them by
an octopus.
The important thing for me is that I need to be able to transfer them
easily, or turn a subdirectory into a subproject or vice versa.
Sorry, we discussed similar things already. It is not necessary to change
the structure. Even more: it makes no sense. Why would you want to have
two or more commit messages for the same revision?
Because the commit affects both the subproject and the master project.
Remember: trees, commits and tags (objects in general) are immutable. You
may think that you just commit a new revision of the subproject, and it is
picked up by the overall project, but that is not the case!
This is why I asked for intended behaviour on commit in a subproject. It
is pretty obvious that the master project would need a new tree object
to reference the new version of the subproject, and hence, a new commit
to keep it all together (and correctly so, since I would like my master
project to refer to that particular version of the subproject that is
known to work).
You can do this like I said: use branches (and possibly a common
GIT_OBJECT_DIRECTORY to save on disk space).
Yes, however that wouldn't cover consistency between the subprojects,
would it?
Simon
The important thing for me is that I need to be able to transfer them easily,
or turn a subdirectory into a subproject or vice versa.
Turning a _snapshot_ of a subproject into a subdirectory is easy: you can
literally just create a subdirectory, copy it there, and it will re-use
all the objects that the subproject uses (ie the top-level project will
have a "tree" entry that just points to the same tree entry as the
top-level commit in the sub-project).
However, while that works as a way to import snapshots, it doesn't work in
any other way. It allows you to share objects with the "real project", and
it's space-efficient etc, but there's no shared history, and you cannot
merge back-and-forth, which is probably what you really want to do.
Quite frankly, you really probably want more of a "git-aware symlink" kind
of thing. I'd really hesitate (in fact, I'd object) to re-use the existing
"tree" type for it, but you're not the only one to have asked for
subproject support, so this is clearly not a odd request.
quoted
Sorry, we discussed similar things already. It is not necessary to change
the structure. Even more: it makes no sense. Why would you want to have two
or more commit messages for the same revision?
Because the commit affects both the subproject and the master project.
What we _could_ do is for you to first do a commit in the "independent"
subproject (it really would be a totally independent git repository in all
ways: you could continue to merge it with other subprojects of the same
type), and then you could commit a new pointer to that subproject in the
master project.
The two would really be fundamentally independent: they'd be two different
git projects, one would just have a strange kind of "symlink" to the
other, which would include a name and the top commit SHA1 of the other
project.
Getting everything to work reasonably seamlessly would be potentially
painful (getting "git diff" to recurse into the subdirectory correctly is
non-trivial: you'd have a separate ".git/index" file for it), but it
sounds doable.
I'd suggest adding a new kind of object ("gitlink") which has some
well-specified format (20-byte SHA1 + ASCII C string "name" - the name
translation to external repository would be done in the .git/config file
of the "outer" project). Then a special file mode to indicate that in the
"struct tree", and support for "git-update-cache" to understand how such
an object is really tied into the "<pathname>/.git/HEAD" file rather than
the rest of the directory contents.
Then a "git fetch" would have to be taught to recursively fetch the other
subproject when the "gitlink" changes.
It should be doable: somebody could try to implement a rough first draft
(maybe not very seamless at first).
Linus
From: Simon Richter <hidden> Date: 2016-06-15 22:42:16
Hi,
Linus Torvalds wrote:
Turning a _snapshot_ of a subproject into a subdirectory is easy: you can
literally just create a subdirectory, copy it there, and it will re-use
all the objects that the subproject uses (ie the top-level project will
have a "tree" entry that just points to the same tree entry as the
top-level commit in the sub-project).
Exactly. My proposal is to allow the tree object to point to the
toplevel commit object directly, thus importing the entire project[1].
However, while that works as a way to import snapshots, it doesn't work in
any other way. It allows you to share objects with the "real project", and
it's space-efficient etc, but there's no shared history, and you cannot
merge back-and-forth, which is probably what you really want to do.
Well, the history cannot be really shared, as turning subtrees to
projects and vice versa is a valid use case (and in fact something I do
pretty often in my projects, as various "helper" classes evolve into
"utility" libraries). Since the subproject needs to be self-contained,
the history before it became a separate project will be difficult to
represent, to say the least.
Quite frankly, you really probably want more of a "git-aware symlink" kind
of thing. I'd really hesitate (in fact, I'd object) to re-use the existing
"tree" type for it, but you're not the only one to have asked for
subproject support, so this is clearly not a odd request.
[...]
What we _could_ do is for you to first do a commit in the "independent"
subproject (it really would be a totally independent git repository in all
ways: you could continue to merge it with other subprojects of the same
type), and then you could commit a new pointer to that subproject in the
master project.
Exactly. The questions I posed in the last paragraph of the initial
mail, rewritten for clarity, would be
- "should cg-commit automatically create a commit in the master
project when a change in the subproject is committed?", and
- "should cg-commit automatically commit all changes to subprojects
when a path that has been listed on the command line contains a
subproject?".
There are three cases, basically:
- change to subproject, part of a larger set of changes, not ready for
prime time: A commit in the subproject, master left alone (obviously,
the directory would show as "modified".
- change to subproject, fixing a bug that affects the master project.
You'd expect these to happen often, as I could fix stuff that the master
doesn't care about in another tree. In this case, you'd want a new
commit to happen in the master as well, for everyone to enjoy.
- change to subproject and master that need to go in sync, like
renaming a configure option. Obviously, this can also happen after an
update of the subproject, so creating a new commit on the master after
an update is bad, but normal behaviour for update would be to merge and
create a commit instantly if there were no conflicts.
The two would really be fundamentally independent: they'd be two different
git projects, one would just have a strange kind of "symlink" to the
other, which would include a name and the top commit SHA1 of the other
project.
Well, that would be exactly what the tree contains. One could do an
additional level of indirection with another object that just points to
an sha1 (because its name is given by the tree referencing it).
Having such an object would mainly have the advantage of being extensible.
I'd suggest adding a new kind of object ("gitlink") which has some
well-specified format (20-byte SHA1 + ASCII C string "name" - the name
translation to external repository would be done in the .git/config file
of the "outer" project).
Well, most people would likely not care about the external repository of
the subproject, as they can get all the objects from the master project.
I'm not even sure the subproject needs an "origin" branch by default, as
you can push changes to the master project's maintainers who would then
eventually push them on to the subproject's maintainers (in fact I
believe it's vital to be able to keep changes to the subproject in the
master project so development can go on while the subproject maintainers
review the changes.
Then a special file mode to indicate that in the
"struct tree", and support for "git-update-cache" to understand how such
an object is really tied into the "<pathname>/.git/HEAD" file rather than
the rest of the directory contents.
That sounds pretty simple actually.
Then a "git fetch" would have to be taught to recursively fetch the other
subproject when the "gitlink" changes.
I think that would be mostly implicit if we were to use a direct
tree->commit reference, but can be implemented trivially even for the
link objects.
It should be doable: somebody could try to implement a rough first draft
(maybe not very seamless at first).
Indeed. I just wanted to have rough use cases thrown at me before I even
think of implementing something like it.
Simon
[1] There is a slight problem with that approach: When you cherry-pick
changes into a subproject (like I did today with the asm-arm/uaccess.h
constness fix), the subproject will have a separate branch whose head is
known to the outer project only. When the subproject gets merged with
its origin later, that branch is no longer needed, and it makes a lot of
sense to have the master project reference origin again. This means that
if you look at the history of the inner project from the POV of the
outer, the new commit is no longer a descendant of the old, but in fact
it may be a good idea to attempt a fast-forward merge nevertheless as
going through the common ancestor is very likely to cause conflicts
here, and those conflicts have already been resolved, or you wouldn't be
seeing an updated subproject link (i.e. you just want to preserve local
changes and stuff committed on top of the old reference).
Exactly. The questions I posed in the last paragraph of the initial mail,
rewritten for clarity, would be
- "should cg-commit automatically create a commit in the master project when
a change in the subproject is committed?", and
No.
Don't commit in the "parent" thing automatically. The sub-project should
be as independent as possible, and you should see a commit to that to be
nothing more than "editing" a regular file in the top-level project.
In many ways, if you decide to look into doing a "gitlink" kind of object,
that object really _does_ conceptually point to the .git/HEAD file in the
subproject. So when you do a commit in the subproject, conceptually that
is no different from editing the .git/HEAD.
Then, when you want to commit the _dependency_ of the top-level project on
the sub-project, you commit in the top level. That commit probably does
other things too: it probably also commits the code in the top level that
now depends on the sub-project changes.
So don't tie the two together any more than necessary. My suggested usage
case has the big advantage that the sub-project is much less tightly
coupled, so you can do things like "git pull" _inside_ the subproject, to
update it, and then do a big compile in the top-level project to reflect
the changes (and perhaps update stuff at the top level to conform to
changes in the sub-project), and then commit in the top independently
(which will now automatically pick up the changes to .git/HEAD that "git
pull" did on the subproject).
- "should cg-commit automatically commit all changes to subprojects when a
path that has been listed on the command line contains a subproject?".
Again, I'd really suggest not. If you keep the "gitlink" really meaning
the ".git/HEAD" contents of the subproject (which is a good semantic
rule), then if there is any dirty state in the sub-project, it is totally
irrelevant to a "git commit". Because it's dirty, it's not part of of
.git/HEAD yet.
Now, obviously you should make "git status" _talk_ about the fact that the
sub-project is dirty, so that the committer sees that the sub-project
needs committing first (the same way "git status" now informs git commit
about dirty files that haven't been updated).
(I'm saying "git" here all the time rather than cg-, because not only
don't I know cogito very well, I think you want to do most of the core at
the git level, and just teach cg about the new capability).
Linus
From: Alexander Litvinov <hidden> Date: 2016-06-15 22:42:16
On Wednesday 11 January 2006 21:58, Simon Richter wrote:
Hello,
one thing that I have been missing so far in all SCM systems apart from
CVS (and there it's just coincidence) is the ability to include a
project as part of a bigger project.
I really miss this feature. This is the last stopper for moving from CVS to
git for out project.
From: Martin Langhoff <hidden> Date: 2016-06-15 22:42:16
On 1/12/06, Alexander Litvinov [off-list ref] wrote:
On Wednesday 11 January 2006 21:58, Simon Richter wrote:
quoted
Hello,
one thing that I have been missing so far in all SCM systems apart from
CVS (and there it's just coincidence) is the ability to include a
project as part of a bigger project.
I really miss this feature. This is the last stopper for moving from CVS to
git for out project.
What about using nested checkouts? They work great with git as-is,
just add an .gitignore file.
As Linus points out, there are many good reasons why a top-level
commit should _not_ commit the nested subproject. And once you are
observing that rule, what's left then? git status and git diff <HEAD>
can show an aggregate of top-level and nested subprojects, but that's
ease-of-use -- not something only.
What is your show stopper?
cheers,
martin
From: Alexander Litvinov <hidden> Date: 2016-06-15 22:42:16
On Thursday 12 January 2006 10:46, Martin Langhoff wrote:
quoted
I really miss this feature. This is the last stopper for moving from CVS
to git for out project.
What about using nested checkouts? They work great with git as-is,
just add an .gitignore file.
As Linus points out, there are many good reasons why a top-level
commit should _not_ commit the nested subproject. And once you are
observing that rule, what's left then? git status and git diff <HEAD>
can show an aggregate of top-level and nested subprojects, but that's
ease-of-use -- not something only.
What is your show stopper?
I would agree to make separate commits for each sub project.
1. I need to have ability to make tags, branches thru all subprojects.
2. Update (pull) sould update each subproject, it is hard to update them by
hands.
3. The need of some sort of checkout script (can be solved by storing this
script in base project, but it would be much nicer allow git fetch all
subprojects)
Nothing else I can imagine.
From: Martin Langhoff <hidden> Date: 2016-06-15 22:42:16
On 1/12/06, Alexander Litvinov [off-list ref] wrote:
quoted
What is your show stopper?
I would agree to make separate commits for each sub project.
1. I need to have ability to make tags, branches thru all subprojects.
I suspect that this is a bad idea -- for the same reason as committing
to a subproject is a bad idea. The subprojects most likely have their
own external repositories -- and lifecycles of their own. The same
headname/branchname won't do.
2. Update (pull) sould update each subproject, it is hard to update them by
hands.
A simple shellscript can help you here.
3. The need of some sort of checkout script (can be solved by storing this
script in base project, but it would be much nicer allow git fetch all
subprojects)
As you say, a bootstrapping shellscript can sort this out.
Sounds quite doable ;-)
(have to warn you though -- git is quite addictive. there's no going back...)
cheers,
martin
On Thu, 12 Jan 2006 11:25:33 +0600, Alexander Litvinov wrote:
On Thursday 12 January 2006 10:46, Martin Langhoff wrote:
quoted
quoted
I really miss this feature. This is the last stopper for moving from CVS
to git for out project.
What about using nested checkouts? They work great with git as-is,
just add an .gitignore file.
As Linus points out, there are many good reasons why a top-level
commit should _not_ commit the nested subproject. And once you are
observing that rule, what's left then? git status and git diff <HEAD>
can show an aggregate of top-level and nested subprojects, but that's
ease-of-use -- not something only.
What is your show stopper?
I would agree to make separate commits for each sub project.
1. I need to have ability to make tags, branches thru all subprojects.
2. Update (pull) sould update each subproject, it is hard to update them by
hands.
3. The need of some sort of checkout script (can be solved by storing this
script in base project, but it would be much nicer allow git fetch all
subprojects)
Nothing else I can imagine.
From: Alexander Litvinov <hidden> Date: 2016-06-15 22:42:16
quoted
1. I need to have ability to make tags, branches thru all subprojects.
I suspect that this is a bad idea -- for the same reason as committing
to a subproject is a bad idea. The subprojects most likely have their
own external repositories -- and lifecycles of their own. The same
headname/branchname won't do.
This is one main idea of supporing subprojects. Everything else I already can
do. I want to be able to make tag over composite project and be able to fetch
tagged files later. The same with branches.
I cleary understand if I made tag/branch on subproject outside my composite
project I will not be able to work with it - this is ok.
But tag/branches on whole composite project is "the must".
I hope it is possible to teach git (or may be something else) to scan all
subprojects and fetch common tags/branches and work with them.
From: Alex Riesen <hidden> Date: 2016-06-15 22:42:16
On 1/12/06, Alexander Litvinov [off-list ref] wrote:
quoted
quoted
1. I need to have ability to make tags, branches thru all subprojects.
I suspect that this is a bad idea -- for the same reason as committing
to a subproject is a bad idea. The subprojects most likely have their
own external repositories -- and lifecycles of their own. The same
headname/branchname won't do.
This is one main idea of supporing subprojects. Everything else I already can
do. I want to be able to make tag over composite project and be able to fetch
tagged files later. The same with branches.
I cleary understand if I made tag/branch on subproject outside my composite
project I will not be able to work with it - this is ok.
But tag/branches on whole composite project is "the must".
The Linus' proposal of gitlink will probably help you here: gitlink will be
tagged as well, so you just have to teach git-checkout about checking
out subprojects.
From: Daniel Barkalow <hidden> Date: 2016-06-15 22:42:16
On Thu, 12 Jan 2006, Martin Langhoff wrote:
What about using nested checkouts? They work great with git as-is,
just add an .gitignore file.
As Linus points out, there are many good reasons why a top-level
commit should _not_ commit the nested subproject. And once you are
observing that rule, what's left then? git status and git diff <HEAD>
can show an aggregate of top-level and nested subprojects, but that's
ease-of-use -- not something only.
What is your show stopper?
The core structural thing (which I'm not sure CVS handles) is having each
commit of the outer project specify the commit of the inner project that
it contains in some way. This would be good with CVS, but is vital with
git, because there's no way of estimating it when you don't have a linear
history. (With CVS, you could say that the inner project version for a
given outer project version should be the version that was current when
the outer project was committed. But that isn't well-defined for git.) If
you try to debug anything involving the history, you'd have problems with
choosing versions of the two projects that don't actually match.
-Daniel
*This .sig left intentionally blank*
From: Petr Baudis <hidden> Date: 2016-06-15 22:42:16
Hello,
I've tried to take a different approach - KISS and don't make the
subprojects part of the git-tracked tree but a thing purely local to
your particular checkout. Subprojects are simply listed in
.git/subprojects and various commands are called recursively on them.
- No auto-cloning of subprojects is possible.
- Switching between branches and merging is troublesome in case the
required subprojects arrangement changes inbetween. Now, this is
a matter of taste - I don't see this as a huge problem since you
can make special provisions for that and this should be going to be
so rare that it's not worth optimizing for in my eyes.
+ It's simple.
+ It's flexible. You can have _optional_ subprojects - IIRC e.g.
mplayer can use ffmpeg if it's checked out as a subproject but
will use own copy if it's not there. You can clone subprojects
based e.g. on selected features before compilation. If you do so,
your GIT won't bother you with uncommitted local changes, and you
will not have to filter this out from any other changes you are
going to commit.
The main goal of this is simply to be able to check out bunch of
stuff to subdirectories and make cg-update update all of it, without
any special scripts. ;-)
This patch is just trivial proof-of-concept thing which makes only
cg-update and cg-fetch aware of the subprojects; many commands still
need to be taught about subprojects but some don't - I currently don't
think a recursive cg-merge is a particularily good idea, for one.
I think the good default is to make all read-only commands by default
recursive and all modifying commands by default non-recursive. (And
it might be useful to be able to mark some subprojects read-only.)
How to create a subproject? Simply cg-clone inside a working copy,
it will register it automagically. So far there are no tools to further
maintain the subprojects, though, therefore a mv or rm needs to be
followed by an appropriate modification of parent .git/subprojects.
This is not committed yet - I'm curious about your opinions.
@@ -90,23 +90,13 @@ cg-*patch should be pre-1.0.) * cg-Xfetchprogress showing smooth progress for packfiles+* Enhance subprojects notion+ So far the subprojects support is trivial and prone to user error.+ E.g. cg-add should check if it doesn't poke into a subproject,+ cg-status should list subprojects, etc.-Post 1.0:--* Subprojects- Support a GIT project inside a GIT project:- x/.git- x/foo/bar/.git- x/foo/bar/baz/.git- x/quux/zot/.git-- That means cg-update working recursively and cg-add'n'stuff- checking if there isn't another .git along the path of its- argument.-- Needs more thought, especially wrt. fetching and merging- recursive semantics.+Post 1.0: * Comfortable cg-log Probably make it a real terminal application, not just less
@@ -197,6 +197,10 @@ list_untracked_files() if [ -f "$EXCLUDEFILE" ]; then EXCLUDE[${#EXCLUDE[@]}]="--exclude-from=$EXCLUDEFILE" fi+ EXCLUDEFILE="$_git/subprojects"+ if [ -f "$EXCLUDEFILE" ]; then+ EXCLUDE[${#EXCLUDE[@]}]="--exclude-from=$EXCLUDEFILE"+ fi # This is just for compatibility (2005-09-16). # To be removed later. EXCLUDEFILE="$_git/exclude"
@@ -209,6 +213,29 @@ list_untracked_files() git-ls-files -z --others "${EXCLUDE[@]}" }+# Usage: subprojects_recurse ACTIONNAME COMMAND...+# Run command recursively on subprojects, displaying warning using the+# ACTIONNAME string in case any of them failed.+subprojects_recurse()+{+ [ -s "$_git/subprojects" ] || return 0+ local failures=0 subprj= actionname="$1" s=+ local Actionname="$(echo "$actionname" | perl -pe '$_=ucfirst')"+ shift+ while IFS= read -r subprj; do+ echo "Running $actionname in $subprj..." >&2+ if ( cd "$subprj" && "$@" ); then+ echo "$Actionname in $subprj succeeded." >&2+ else+ echo "$Actionname in $subprj failed!" >&2+ failures=$(($failures+1))+ fi+ done <"$_git/subprojects"+ local s=; if [ $failures -gt 1 ]; then s=s; fi+ [ $failures -gt 0 ] && echo "Warning: $failures subproject $actionname$s failed" >&2+ return $failures+}+ # Usage: showdate SECONDS TIMEZONE [FORMAT] # Display date nicely based on how GIT stores it. # Save the date to $_showdate
@@ -11,6 +11,15 @@ # parameter is omitted, the basename of the source repository is used as the # destination. #+# If you are cloning inside another working tree, you are automatically+# establishing a subproject - that means that when you will update the+# parent project, this project will be auto-updated as well, and in the+# future, certain other operations may also recurse to subprojects. Use the+# -P option to prevent this from becoming a subproject. Also please note+# that the subprojects support is preliminary and subject to change. If you+# remove the subproject later, you must also remove the corresponding entry+# in '.git/subprojects' for now.+# # OPTIONS # ------- # -l:: Symlink the object database when cloning locally
@@ -24,20 +33,28 @@ # Note that you MUST NOT prune repository containing a symlink # or being symlinked to. #+# -P:: Prevent this from becoming a subproject+# If this option is passed, cg-clone will never create a subproject+# even if called inside working tree of another project.+# # -s:: Clone into the current directory # Clone in the current directory instead of creating a new one. # Specifying both -s and a destination directory makes no sense.+# This also implies -P.-USAGE="cg-clone [-l] [-s] LOCATION [DESTDIR]"+USAGE="cg-clone [-l] [-P] [-s] LOCATION [DESTDIR]" _git_repo_unneeded=1 . ${COGITO_LIB}cg-Xlib || exit 1 same_dir= symlink=+may_subproject=1 while optparse; do if optparse -l; then symlink=1+ elif optparse -P; then+ may_subproject= elif optparse -s; then same_dir=1 else
@@ -65,7 +82,13 @@ else location="$location" fi+parentprj=+parentpath= if [ ! "$same_dir" ]; then+ if [ "$may_subproject" ]; then+ parentprj="$(git-rev-parse --git-dir 2>/dev/null)"+ parentpath="$(git-rev-parse --show-prefix 2>/dev/null)$dir"+ fi [ -e "$dir" ] && die "$dir/ already exists" mkdir "$dir" || exit $? cd "$dir" || exit $?
@@ -94,4 +117,14 @@ cp "$_git/refs/heads/origin" "$_git/refs git-update-index --refresh || exit 1+if [ "$parentprj" ]; then+ cd ..+ parentroot="${parentprj%.git}"+ if [ -z "$parentroot" ]; then+ parentroot="$(pwd)"+ fi+ echo "Registering as a subproject of $parentroot..."+ echo "$parentpath" >>"$parentprj"/subprojects+fi+ echo "Cloned to $dir/ (origin $location available as branch \"origin\")"
@@ -28,6 +28,10 @@ # -f:: Force the complete fetch even if the heads are the same. # Force the complete fetch even if the heads are the same. #+# -R:: Do not recurse to subprojects+# Do not recursively fetch the subprojects (see the `cg-clone`+# documentation for more information).+# # -v:: Enable verbosity # Display more verbose output - most notably list all the files # touched by the fetched changes.
@@ -234,12 +238,15 @@ fetch_tags() recovery=+recurse=1 verbose= while optparse; do if optparse -f; then # When forcing, let the fetch tools make more extensive # walk over the dependency tree with --recover. recovery=--recover+ elif optparse -R; then+ recurse= elif optparse -v; then verbose=1 else
@@ -248,9 +255,10 @@ while optparse; do done name="${ARGS[0]}"- [ "$name" ] || { [ -s "$_git/branches/origin" ] && name=origin; }+[ "$recurse" ] && subprojects_recurse "fetch" cg-fetch $recovery $verbose "$name" [ "$name" ] || die "where to fetch from?"+ uri=$(cat "$_git/branches/$name" 2>/dev/null) || die "unknown branch: $name" rembranch=
@@ -25,23 +25,30 @@ # -f:: Force the complete fetch even if the heads are the same. # Force the complete fetch even if the heads are the same. #+# -R:: Do not recurse to subprojects+# Do not recursively update the subprojects (see the `cg-clone`+# documentation for more information).+# # --squash:: Use "squash" merge to record pending commits as a single merge commit # "Squash" merge - condense all the to-be-merged commits to a single # merge commit. This is not to be used lightly; see the cg-merge # documenation for further details.-USAGE="cg-update [-f] [--squash] [BRANCH_NAME]"+USAGE="cg-update [-f] [-R] [--squash] [BRANCH_NAME]" _git_requires_root=1 . ${COGITO_LIB}cg-Xlib || exit 1 force= squash=+recurse=1 while optparse; do if optparse -f; then force=-f elif optparse --squash; then squash=--squash+ elif optparse -R; then+ recurse= else optfail fi
@@ -49,13 +56,14 @@ done name="${ARGS[0]}" [ "$name" ] || { [ -s "$_git/branches/origin" ] && name=origin; }+[ "$recurse" ] && subprojects_recurse "update" cg-update $force $squash "$name" [ "$name" ] || die "where to update from?" # cg-merge can do better decision about fast-forwarding if it sees this. [ -s "$_git/refs/heads/$name" ] && export _cg_orig_head="$(cat "$_git/refs/heads/$name")" if [ -s "$_git/branches/$name" ]; then- cg-fetch $force "$name" || exit 1+ cg-fetch -R $force "$name" || exit 1 else echo "Updating from a local branch." fi
@@ -0,0 +1,54 @@+#!/usr/bin/env bash+#+# Copyright (c) 2005 Petr Baudis+#+test_description="Tests recursive cg-update functionality++Createasubprojectandthentrytocg-update."++../test-lib.sh++mkdirprj1+echofile>prj1/file+test_expect_success'initialize project 1'\+"(cd prj1 && cg-init -I && cg-add file && cg-commit -C -m\"Initial commit\")"++mkdirprj2+echofile>prj2/FILE+test_expect_success'initialize project 2'\+"(cd prj2 && cg-init -I && cg-add FILE && cg-commit -C -m\"Initial commit\")"++test_expect_success'clone project 1'\+"cg-clone prj1 clone"+test_expect_success'clone project 2 as subproject of project 1'\+"(cd clone && cg-clone ../prj2)"++test_expect_success'commit to project 1'\+"(cd prj1 && echo foo >>file && cg-commit -m\"Commit in project 1\")"+test_expect_success'commit to project 2'\+"(cd prj2 && echo bar >>FILE && cg-commit -m\"Commit in project 2\")"++test_expect_success'non-recursive update'\+"(cd clone && cg-update -R)"+test_expect_success'check if the prj1 head was updated in clone'\+"(cmp prj1/.git/refs/heads/master clone/.git/refs/heads/master)"+test_expect_success'check if the prj1 working copy was updated in clone'\+"(cmp prj1/file clone/file)"+test_expect_failure'check if the prj2 head was not updated in clone'\+"(cmp prj2/.git/refs/heads/master clone/prj2/.git/refs/heads/origin)"++test_expect_success'commit to project 1'\+"(cd prj1 && echo baz >>file && cg-commit -m\"Commit in project 1\")"++test_expect_success'recursive update'\+"(cd clone && cg-update)"+test_expect_success'check if the prj1 head was updated in clone'\+"(cmp prj1/.git/refs/heads/master clone/.git/refs/heads/master)"+test_expect_success'check if the prj1 working copy was updated in clone'\+"(cmp prj1/file clone/file)"+test_expect_success'check if the prj2 head was updated in clone'\+"(cmp prj2/.git/refs/heads/master clone/prj2/.git/refs/heads/master)"+test_expect_success'check if the prj2 working copy was updated in clone'\+"(cmp prj2/FILE clone/prj2/FILE)"++test_done
--
Petr "Pasky" Baudis
Stuff: http://pasky.or.cz/
Of the 3 great composers Mozart tells us what it's like to be human,
Beethoven tells us what it's like to be Beethoven and Bach tells us
what it's like to be the universe. -- Douglas Adams
I've tried to take a different approach - KISS and don't make the
subprojects part of the git-tracked tree but a thing purely local to
your particular checkout. Subprojects are simply listed in
.git/subprojects and various commands are called recursively on them.