git submodule update --reset-hard
Dear Git Gurus,
I wondered what would be your take on my wishlist request to add --reset-hard option, which would be very similar to regular "update" which checks out necessary commit, but I want it to remain in the branch.
Rationale: In DataLad we heavily rely on submodules, and we have established easy ways to do some manipulations across full hierarchies of them. E.g. a single command could introduce a good number of commits across deep hierarchy of submodules, e.g. while committing changes within deep submodule, while also doing all necessary commits in the repositories leading to that submodule so the entire tree of them stays in a "clean" state. The difficulty comes when there is a need to just "forget" some changes. The obvious way is to e.g.
git reset --hard PREVIOUS_STATE
in the top level repository. But that leaves all the submodules now in the undesired state. If I do
git submodule update --recursive
I would end up in the detached HEADs within submodules.
What I want is to retain current branch they are at (or may be possible "were in"? reflog records might have that information)
Example:
# Have to use datalad install since git clone --recurse-submodules
# seems to not consider alternative locations for submodules' .git/ # with url being just a relative path, and where submodules aren't # all residing up under toplevel URL .git/
$> datalad install -r http://datasets.datalad.org/labs/gobbini/.git
[INFO ] Cloning http://datasets.datalad.org/labs/gobbini/.git into '/tmp/gobbini'
install(ok): /tmp/gobbini (dataset)
[INFO ] Installing <Dataset path=/tmp/gobbini> recursively
[INFO ] Cloning http://datasets.datalad.org/labs/gobbini/famface/.git into '/tmp/gobbini/famface'
[INFO ] Cloning http://datasets.datalad.org/labs/gobbini/famface/data/.git into '/tmp/gobbini/famface/data'
[INFO ] access to dataset sibling "datasets.datalad.org" not auto-enabled, enable with:
| datalad siblings -d "/tmp/gobbini/famface/data" enable -s datasets.datalad.org
[INFO ] Cloning http://datasets.datalad.org/labs/gobbini/famface/data/scripts/mridefacer/.git [2 other candidates] into '/tmp/gobbini/famface/data/scripts/mridefacer'
action summary:
install (ok: 4)
so I have a hierarchy in a good state and all checked out in master branch
$> cd gobbini $> git submodule status --recursive b9071a6bc9f7665f7c75549c63d29f16d40e8af7 famface (heads/master) e59ba76b42f219bdf14b6b547dd6d9cc0ed5227f famface/data (BIDS-v1.0.1-3-ge59ba76b) 5d8036c0aaeebb448a00df6296ddc9f799efdd1f famface/data/scripts/mridefacer (heads/master) $> git submodule foreach --recursive cat .git/HEAD Entering 'famface' ref: refs/heads/master Entering 'famface/data' ref: refs/heads/master Entering 'famface/data/scripts/mridefacer' ref: refs/heads/master
and if I do roll back
$> git reset --hard HEAD^^^ HEAD is now at 9b4296d [DATALAD] aggregated meta data changes on filesystem: famface | 2 +-
and default update --recursive
$> git submodule update --recursive Submodule path 'famface': checked out '2569ab436501a832d35afbbe9cc20ffeb6077eb1' Submodule path 'famface/data': checked out 'f1e8c9b8b025c311424283b9711efc6bc906ba2b' Submodule path 'famface/data/scripts/mridefacer': checked out '49b0fe42696724c2a8492f999736056e51b77358'
I end up in detached HEADs
$> git submodule status --recursive 2569ab436501a832d35afbbe9cc20ffeb6077eb1 famface (2569ab4) f1e8c9b8b025c311424283b9711efc6bc906ba2b famface/data (BIDS-v1.0.1) 49b0fe42696724c2a8492f999736056e51b77358 famface/data/scripts/mridefacer (49b0fe4)
I do see that there is a "custom command" way to do it via "submodule.<name>.update" config setting, but that is not easy to use for my case since all the <name> would be different to specify !git reset --hard for all of them via config option and I could not find any way to "glob" config (like submodule.*.update). But in effect that is probably what I need:
# restarting from a clean state here $> git -c submodule.famface.update='!git reset --hard' submodule update --recursive HEAD is now at 2569ab4 [DATALAD] aggregated meta data Submodule path 'famface': 'git reset --hard 2569ab436501a832d35afbbe9cc20ffeb6077eb1' Submodule path 'famface/data': checked out 'f1e8c9b8b025c311424283b9711efc6bc906ba2b' Submodule path 'famface/data/scripts/mridefacer': checked out '49b0fe42696724c2a8492f999736056e51b77358'
$> git submodule status --recursive 2569ab436501a832d35afbbe9cc20ffeb6077eb1 famface (heads/master) f1e8c9b8b025c311424283b9711efc6bc906ba2b famface/data (BIDS-v1.0.1) 49b0fe42696724c2a8492f999736056e51b77358 famface/data/scripts/mridefacer (49b0fe4)
Corner cases I see which might make it trickier for a full blown solution (might be relevant to current state as well for other strategies):
- If between those commits we got an additional submodule added (in immediate repository or within one of the subdatasets), ideally it should also be wiped out
Signature · 5 lines
Yaroslav O. Halchenko Center for Open Neuroscience http://centerforopenneuroscience.org Dartmouth College, 419 Moore Hall, Hinman Box 6207, Hanover, NH 03755 Phone: +1 (603) 646-9834 Fax: +1 (603) 646-1419 WWW: http://www.linkedin.com/in/yarik
Quoted 6 lines · Yaroslav Halchenko
Dear Git Gurus,
I wondered what would be your take on my wishlist request to add --reset-hard option, which would be very similar to regular "update" which checks out necessary commit, but I want it to remain in the branch.
What if the branch differs from the sha1 recorded in the superproject?
Quoted 9 lines
Rationale: In DataLad we heavily rely on submodules, and we have established easy ways to do some manipulations across full hierarchies of them. E.g. a single command could introduce a good number of commits across deep hierarchy of submodules, e.g. while committing changes within deep submodule, while also doing all necessary commits in the repositories leading to that submodule so the entire tree of them stays in a "clean" state. The difficulty comes when there is a need to just "forget" some changes. The obvious way is to e.g.
git reset --hard PREVIOUS_STATE
git reset --hard --recurse-submodules HEAD
would do the trick
Quoted 2 lines
in the top level repository. But that leaves all the submodules now in the undesired state. If I do
undesirable in the sense of still having local changes (that is what the above reset with --recurse would fix) or changed the branch state? (i.e. is detached but was on a branch before?)
Quoted 6 lines
git submodule update --recursiveI would end up in the detached HEADs within submodules.
What I want is to retain current branch they are at (or may be possible "were in"? reflog records might have that information)
So something like
git submodule foreach --recursive git reset --hard
?
You may be interested in
https://public-inbox.org/git/[email protected]/
which introduces a switch submodule.repoLike [ = true], which when set would not detach HEAD in submodules.
Can you say more about the first question above:
Would you typically have situations where the submodule branch is out of sync with the superproject and how do you deal with that?
Adding another mode to git submodule update sounds reasonable to me, too.
Stefan
Quoted 9 lines · Stefan Beller
Quoted 5 lines · Yaroslav Halchenko
Dear Git Gurus,
I wondered what would be your take on my wishlist request to add --reset-hard option, which would be very similar to regular "update" which checks out necessary commit, but I want it to remain in the branch.
What if the branch differs from the sha1 recorded in the superproject?
git reset --hard itself is an operation which should be done with some level of competence in doing "the right thing" by calling it. You can hop branches even in current (without any submodules in question) repository with it and cause as much chaos as you desire.
If desired though, a number of prevention mechanisms could be in place (but would require option(s) to overcome) to allow submodule to be reset --hard'ed only when some conditions met (e.g. only to the commit which is among parent commits path of the current branch). This way wild hops would be prevented, although you might still end up in some feature branch. But since "reset --hard" itself doesn't have any safe-guards, I do not really think they should be implemented here either.
Quoted 8 lines
Quoted 7 lines
Rationale: In DataLad we heavily rely on submodules, and we have established easy ways to do some manipulations across full hierarchies of them. E.g. a single command could introduce a good number of commits across deep hierarchy of submodules, e.g. while committing changes within deep submodule, while also doing all necessary commits in the repositories leading to that submodule so the entire tree of them stays in a "clean" state. The difficulty comes when there is a need to just "forget" some changes. The obvious way is to e.g.
Quoted 5 lines
Quoted 1 line
git reset --hard PREVIOUS_STATEgit reset --hard --recurse-submodules HEADwould do the trick
it does indeed some trick(s) but not all seems to be the ones I desire:
1. Seems to migrate submodule's .git directories into the top level .git/modules
$> git reset --hard --recurse-submodules HEAD^^^
Migrating git directory of 'famface' from '/tmp/gobbini/famface/.git' to '/tmp/gobbini/.git/modules/famface'
Migrating git directory of 'famface/data' from '/tmp/gobbini/famface/data/.git' to '/tmp/gobbini/.git/modules/famface/modules/data'
Migrating git directory of 'famface/data/scripts/mridefacer' from '/tmp/gobbini/famface/data/scripts/mridefacer/.git' to '/tmp/gobbini/.git/modules/famface/modules/data/modules/scripts/mridefacer' HEAD is now at 9b4296d [DATALAD] aggregated meta data
we might eventually adopt this default already for years model (git annex seems to be ok, in that it then replaces .git symlink file with the actual symlink .git -> ../../.git/modules/... So things seems to keep working for annex)
2. It still does the detached HEAD for me
$> git submodule status --recursive 2569ab436501a832d35afbbe9cc20ffeb6077eb1 famface (2569ab4) f1e8c9b8b025c311424283b9711efc6bc906ba2b famface/data (BIDS-v1.0.1) 49b0fe42696724c2a8492f999736056e51b77358 famface/data/scripts/mridefacer (49b0fe4)
Quoted 6 lines
Quoted 2 lines
in the top level repository. But that leaves all the submodules now in the undesired state. If I do
undesirable in the sense of still having local changes (that is what the above reset with
--recursewould fix) or changed the branch state? (i.e. is detached but was on a branch before?)
right -- I meant the local changes and indeed reset --recurse-submodules indeed seems to recurse nicely. Then the undesired effect remaining only the detached HEAD
Quoted 4 lines
Quoted 3 lines
git submodule update --recursiveI would end up in the detached HEADs within submodules.
Quoted 8 lines
Quoted 2 lines
What I want is to retain current branch they are at (or may be possible "were in"? reflog records might have that information)
So something like
git submodule foreach --recursive git reset --hard
?
not quite -- this would just kill all local changes within each submodule, not to reset it to the desired state, which wouldn't be specified in such invocation, and is only known to the repo containing it
Quoted 4 lines
You may be interested in
https://public-inbox.org/git/[email protected]/
which introduces a switchsubmodule.repoLike [ = true], which when set would not detach HEAD in submodules.
Thanks! looks interesting -- was there more discussion/activity beyond those 5 posts in the thread?
https://public-inbox.org/git/[email protected]/#r
This feature might indeed come handy but if I got it right, it is somewhat complimentary to just having submodule update --reset-hard . E.g. submodules might be in different branches (if I am not tracking based on branch names), so I would not want a recursive checkout with -b|-B. But we would indeed benefit from such functionality, since this difficulty of managing branches of submodules I think would be elevated with it! (e.g. in one use case we probably will end up with a few thousands of submodules, and at least 3 branches in each which would need to be in sync, and typically you wouldn't want different branches to be checked out in different submodules)
Quoted 4 lines
Can you say more about the first question above:
Would you typically have situations where the submodule branch is out of sync with the superproject and how do you deal with that?
typically I do not have anything out of sync. My primary use-case is to "cancel" recent changes in the entire tree of repositories. I guess for my use case, instead of needing two commands
git reset --hard PREVIOUSPOINT git submodule update --reset--hard --recursive
I wish there was just one
git reset --hard --recursive PREVIOUSPOINT
but I felt that submodule update might be a better starting point
since it already provides different modes for update. If I was even greedier, I would have asked for
git revert --recursive <commit>...
git rebase --recursive [-i] ...
which I also frequently desire (could elaborate on the use cases etc).
NB or --recurse-submodules to avoid confusion with recursive merge strategy?
But for a complete answer -- if submodule branch is ahead of the superproject's record, I just commit new state for it in superproject. Or if I see that all what I have done was actually a throw away -- "reset --hard" to that needed state, again manually in submodule. With submodule update --reset-hard
--recursive or git reset --hard --recursive I would be just ready
regardless of the depth and complexity of the hierarchy ;-)
Quoted 2 lines
Adding another mode to
git submodule updatesounds reasonable to me, too.
cool, thanks!
Signature · 5 lines
Yaroslav O. Halchenko Center for Open Neuroscience http://centerforopenneuroscience.org Dartmouth College, 419 Moore Hall, Hinman Box 6207, Hanover, NH 03755 Phone: +1 (603) 646-9834 Fax: +1 (603) 646-1419 WWW: http://www.linkedin.com/in/yarik
Quoted 12 lines · Yaroslav Halchenko
Quoted 7 lines · Stefan Beller
Quoted 5 lines · Yaroslav Halchenko
Dear Git Gurus,
I wondered what would be your take on my wishlist request to add --reset-hard option, which would be very similar to regular "update" which checks out necessary commit, but I want it to remain in the branch.
Quoted 6 lines
Quoted 1 line
What if the branch differs from the sha1 recorded in the superproject?
git reset --hard itself is an operation which should be done with some level of competence in doing "the right thing" by calling it. You can hop branches even in current (without any submodules in question) repository with it and cause as much chaos as you desire.
Right.
git reset --hard would the branch (as well as the working tree) to the given sha1, which is confusing as submodules get involved.
The Right Thing as of now is the sha1 as found in the superprojects gitlink. But as that can be different from any branch in the submodule, we'd rather detach the HEAD to make it deterministic.
There was a proposal to "re-attach HEAD" in the submodule, i.e. if the branch branch points at the same commit, we don't need a detached HEAD, but could go with the branch instead.
Quoted 7 lines
If desired though, a number of prevention mechanisms could be in place (but would require option(s) to overcome) to allow submodule to be reset --hard'ed only when some conditions met (e.g. only to the commit which is among parent commits path of the current branch). This way wild hops would be prevented, although you might still end up in some feature branch. But since "reset --hard" itself doesn't have any safe-guards, I do not really think they should be implemented here either.
So are you looking for
a) "stay on submodule branch (i.e. HEAD still points at $branch), and reset --hard"
such that the submodule has a clean index and at that $branch or
b) "stay on submodule branch (i.e. HEAD still points at $branch), but $branch is set to the gitlink from the superproject, and then a reset --hard
will have the worktree
set to it as well.
(a) is what the referenced submodule.repoLike option implements.
I'd understand the desire for (b) as well, as it is a "real" hard reset on the superproject level, without detaching branches.
Quoted 6 lines
Quoted 1 line
git reset --hard --recurse-submodules HEADit does indeed some trick(s) but not all seems to be the ones I desire:
1. Seems to migrate submodule's .git directories into the top level .git/modules
Ah yes, that happens too. This will help once you want to git-rm a submodule and checkout states before and after.
Quoted 7 lines
Quoted 3 lines
undesirable in the sense of still having local changes (that is what the above reset with
--recursewould fix) or changed the branch state? (i.e. is detached but was on a branch before?)right -- I meant the local changes and indeed reset --recurse-submodules indeed seems to recurse nicely. Then the undesired effect remaining only the detached HEAD
For that we may want to revive discussions in
https://public-inbox.org/git/[email protected]/
Quoted 9 lines
Quoted 8 lines
Quoted 6 lines
git submodule update --recursiveI would end up in the detached HEADs within submodules.
What I want is to retain current branch they are at (or may be possible "were in"? reflog records might have that information)
So something like
Quoted 7 lines
Quoted 3 lines
git submodule foreach --recursive git reset --hard
?
not quite -- this would just kill all local changes within each submodule, not to reset it to the desired state, which wouldn't be specified in such invocation, and is only known to the repo containing it
With this answer it sounds like you'd want (b) from above.
Quoted 7 lines
Quoted 4 lines
You may be interested in
https://public-inbox.org/git/[email protected]/
which introduces a switchsubmodule.repoLike [ = true], which when set would not detach HEAD in submodules.Thanks! looks interesting -- was there more discussion/activity beyond those 5 posts in the thread?
Unfortunately there was not.
Quoted 19 lines
This feature might indeed come handy but if I got it right, it is somewhat complimentary to just having submodule update --reset-hard . E.g. submodules might be in different branches (if I am not tracking based on branch names), so I would not want a recursive checkout with -b|-B. But we would indeed benefit from such functionality, since this difficulty of managing branches of submodules I think would be elevated with it! (e.g. in one use case we probably will end up with a few thousands of submodules, and at least 3 branches in each which would need to be in sync, and typically you wouldn't want different branches to be checked out in different submodules)
Quoted 4 lines
Can you say more about the first question above:
Would you typically have situations where the submodule branch is out of sync with the superproject and how do you deal with that?typically I do not have anything out of sync. My primary use-case is to "cancel" recent changes in the entire tree of repositories. I guess for my use case, instead of needing two commands
Quoted 6 lines
git reset --hard PREVIOUSPOINT git submodule update --reset--hard --recursiveI wish there was just one
git reset --hard --recursive PREVIOUSPOINT
Maybe this could learn options like
git reset --hard --recursive=hard,keep-branch PREVIOUSPOINT
which then could be put into options like
git config reset.recurseSubmodules hard,keep-branch &&
# maybe not needed, depending on the exact meaning # of reset.recurseSubmodules: git config submodule.recurse
and then
git reset --hard PREVIOUS
would do what you'd desire.
Quoted 8 lines
but I felt that submodule update might be a better starting point
since it already provides different modes for update. If I was even greedier, I would have asked forgit revert --recursive <commit>...
git rebase --recursive [-i] ...which I also frequently desire (could elaborate on the use cases etc).
These would be nice to have. It would be nice if you'd elaborate on the use cases for future reference in the mailing list archive. :-)
Quoted 3 lines
NB or --recurse-submodules to avoid confusion with recursive merge strategy?
... and sometimes recursing in the file system, c.f. ls-tree -r.
Hi Stefan,
Thanks for the dialogue! Replies are embedded below.
Quoted 10 lines · Stefan Beller
...
Quoted 6 lines
Quoted 1 line
What if the branch differs from the sha1 recorded in the superproject?
git reset --hard itself is an operation which should be done with some level of competence in doing "the right thing" by calling it. You can hop branches even in current (without any submodules in question) repository with it and cause as much chaos as you desire.
Right.
Quoted 7 lines
git reset --hard would the branch (as well as the working tree) to the given sha1, which is confusing as submodules get involved.
The Right Thing as of now is the sha1 as found in the superprojects gitlink. But as that can be different from any branch in the submodule, we'd rather detach the HEAD to make it deterministic.
yeap, makes total sense to be the thing do that by default ;-)
Quoted 3 lines
There was a proposal to "re-attach HEAD" in the submodule, i.e. if the branch branch points at the same commit, we don't need a detached HEAD, but could go with the branch instead.
if I got the idea right, if we are talking about any branch, it would also non-deterministic since who knows what left over branch(es) point to that commit. Not sure if I would have used that ;)
Quoted 8 lines
Quoted 7 lines
If desired though, a number of prevention mechanisms could be in place (but would require option(s) to overcome) to allow submodule to be reset --hard'ed only when some conditions met (e.g. only to the commit which is among parent commits path of the current branch). This way wild hops would be prevented, although you might still end up in some feature branch. But since "reset --hard" itself doesn't have any safe-guards, I do not really think they should be implemented here either.
Quoted 7 lines
So are you looking for
a) "stay on submodule branch (i.e. HEAD still points at $branch), and reset --hard" such that the submodule has a clean index and at that $branch or
b) "stay on submodule branch (i.e. HEAD still points at $branch), but $branch is set to the gitlink from the superproject, and then a reset --hard will have the worktree set to it as well.
yes!
NB "gitlink" -- just now discovered the thing for me. Thought it would be called a subproject echoing what git diff/log -p shows for submodule commits.
Quoted 1 line
(a) is what the referenced submodule.repoLike option implements.
sounds like it indeed, thanks for spelling out
Quoted 2 lines
I'd understand the desire for (b) as well, as it is a "real" hard reset on the superproject level, without detaching branches.
yeap
Quoted 9 lines
Quoted 6 lines
Quoted 1 line
git reset --hard --recurse-submodules HEADit does indeed some trick(s) but not all seems to be the ones I desire:
1. Seems to migrate submodule's .git directories into the top level .git/modules
Ah yes, that happens too. This will help once you want to git-rm a submodule and checkout states before and after.
yeap ;-)
Quoted 10 lines
Quoted 7 lines
Quoted 3 lines
undesirable in the sense of still having local changes (that is what the above reset with
--recursewould fix) or changed the branch state? (i.e. is detached but was on a branch before?)right -- I meant the local changes and indeed reset --recurse-submodules indeed seems to recurse nicely. Then the undesired effect remaining only the detached HEAD
For that we may want to revive discussions in
https://public-inbox.org/git/[email protected]/
well, isn't that one requires a branch to be specified in .gitmodules?
Quoted 11 lines
Quoted 10 lines
Quoted 10 lines
Quoted 6 lines
git submodule update --recursiveI would end up in the detached HEADs within submodules.
What I want is to retain current branch they are at (or may be possible "were in"? reflog records might have that information)
So something like
git submodule foreach --recursive git reset --hard
Quoted 7 lines
Quoted 5 lines
Quoted 1 line
?
not quite -- this would just kill all local changes within each submodule, not to reset it to the desired state, which wouldn't be specified in such invocation, and is only known to the repo containing it
With this answer it sounds like you'd want (b) from above.
yeap
Quoted 9 lines
Quoted 7 lines
Quoted 4 lines
You may be interested in
https://public-inbox.org/git/[email protected]/
which introduces a switchsubmodule.repoLike [ = true], which when set would not detach HEAD in submodules.Thanks! looks interesting -- was there more discussion/activity beyond those 5 posts in the thread?
Unfortunately there was not.
pity
Quoted 22 lines
Quoted 21 lines
This feature might indeed come handy but if I got it right, it is somewhat complimentary to just having submodule update --reset-hard . E.g. submodules might be in different branches (if I am not tracking based on branch names), so I would not want a recursive checkout with -b|-B. But we would indeed benefit from such functionality, since this difficulty of managing branches of submodules I think would be elevated with it! (e.g. in one use case we probably will end up with a few thousands of submodules, and at least 3 branches in each which would need to be in sync, and typically you wouldn't want different branches to be checked out in different submodules)
Quoted 4 lines
Can you say more about the first question above:
Would you typically have situations where the submodule branch is out of sync with the superproject and how do you deal with that?typically I do not have anything out of sync. My primary use-case is to "cancel" recent changes in the entire tree of repositories. I guess for my use case, instead of needing two commands
git reset --hard PREVIOUSPOINT git submodule update --reset--hard --recursive
Quoted 7 lines
Quoted 3 lines
I wish there was just one
git reset --hard --recursive PREVIOUSPOINTMaybe this could learn options like
git reset --hard --recursive=hard,keep-branch PREVIOUSPOINT
'keep-branch' (given aforementioned keeping the specified in .gitmodules branch) might be confusing. Also what if a submodule already in a detached HEAD? IMHO --recursive=hard, and just saying that it would do "reset --hard", is imho sufficient. (that is why I like pure
--reset hard since it doesn't care and neither does anything to the
branch)
Quoted 7 lines
which then could be put into options like
git config reset.recurseSubmodules hard,keep-branch &&
# maybe not needed, depending on the exact meaning # of reset.recurseSubmodules: git config submodule.recurse
Quoted 5 lines
and then
git reset --hard PREVIOUSwould do what you'd desire.
you mean
git reset --hard --recurse-submodules PREVIOUS
in principle overall I would love to have it, besides not sure what other than 'hard' could be there, and what 'keep-branch' would exactly do ;-)
Quoted 11 lines
Quoted 8 lines
but I felt that submodule update might be a better starting point
since it already provides different modes for update. If I was even greedier, I would have asked forgit revert --recursive <commit>...
git rebase --recursive [-i] ...which I also frequently desire (could elaborate on the use cases etc).
These would be nice to have. It would be nice if you'd elaborate on the use cases for future reference in the mailing list archive. :-)
ok, will try to do so ;-) In summary: they are just a logical extension of git support for submodules for anyone actively working with submodules to keep entire tree in sync. Then quite often the need for reverting a specific commit (which also has changes reflected in submodules) arises. The same with rebase, especially to trim away some no longer desired changes reflected in submodules.
the initial "git submodule update --reset-hard" is pretty much a crude workaround for some of those cases, so I would just go earlier in the history, and redo some things, whenever I could just drop or revert some selected set of commits.
Quoted 4 lines
Quoted 2 lines
NB or --recurse-submodules to avoid confusion with recursive merge strategy?
... and sometimes recursing in the file system, c.f.
ls-tree -r.
ah... so it is only submodule command which has --recursive, and the
rest have --recurse-submodules when talking about recursing into
submodules?
Signature · 5 lines
Yaroslav O. Halchenko Center for Open Neuroscience http://centerforopenneuroscience.org Dartmouth College, 419 Moore Hall, Hinman Box 6207, Hanover, NH 03755 Phone: +1 (603) 646-9834 Fax: +1 (603) 646-1419 WWW: http://www.linkedin.com/in/yarik
Quoted 7 lines · Yaroslav Halchenko
Quoted 3 lines
There was a proposal to "re-attach HEAD" in the submodule, i.e. if the branch branch points at the same commit, we don't need a detached HEAD, but could go with the branch instead.
if I got the idea right, if we are talking about any branch, it would also non-deterministic since who knows what left over branch(es) point to that commit. Not sure if I would have used that ;)
I would think we'd rather want to have it deterministic, i.e. something like
1) record branch name of the submodule
2) update submodules HEAD to to superprojects gitlink
3) if recorded branch (1) matches the sha1 of detached HEAD, have HEAD point to the branch instead.
You notice a small inefficiency here as we write HEAD twice, so it could be reworded as:
1) compare superprojects gitlink with the submodules branch 2a) if equal, set submodules HEAD to branch
2b) if unequal set HEAD to gitlink value, resulting in detached HEAD
Note that this idea of reattaching reflects the idea (a) below.
Quoted 10 lines
Quoted 6 lines
a) "stay on submodule branch (i.e. HEAD still points at $branch), and reset --hard" such that the submodule has a clean index and at that $branch or
b) "stay on submodule branch (i.e. HEAD still points at $branch), but $branch is set to the gitlink from the superproject, and then a reset --hard will have the worktree set to it as well.NB "gitlink" -- just now discovered the thing for me. Thought it would be called a subproject echoing what git diff/log -p shows for submodule commits.
The terminology is messy:
The internal representation in Gits object model is a "gitlink" entry in a tree object. Once we have a .gitmodules entry, we call it submodule.
The term 'subproject' is a historic artifact and will likely not be changed in the diff output (or format-patch), because these diffs can be applied using git-am for example. That makes the diff output effectively a transport protocol, and changing protocols is hard if you have no versioning in them.
More in https://git-scm.com/docs/gitsubmodules (a rather recent new write of a man page, going into concepts).
Quoted 8 lines
Quoted 6 lines
Quoted 3 lines
right -- I meant the local changes and indeed reset --recurse-submodules indeed seems to recurse nicely. Then the undesired effect remaining only the detached HEAD
For that we may want to revive discussions in
https://public-inbox.org/git/[email protected]/well, isn't that one requires a branch to be specified in .gitmodules?
Ah good point.
Quoted 8 lines
Quoted 1 line
git reset --hard --recursive=hard,keep-branch PREVIOUSPOINT'keep-branch' (given aforementioned keeping the specified in .gitmodules branch) might be confusing. Also what if a submodule already in a detached HEAD? IMHO --recursive=hard, and just saying that it would do "reset --hard", is imho sufficient. (that is why I like pure
--reset hard since it doesn't care and neither does anything to the
branch)
For that we might want to first do the
git submodule update --reset-hard
which runs reset --hard inside the submodule, no matter which branch the submodule is on (if any) and resets to the given superproject sha1.
See git-submodule.sh in git.git[1] in cmd_update. We'd need to add a command line flag (--reset-hard would be the obvious choice?) which would set the update variable, which then is evaluated to what needs to be done in the submodule, which in that case would be the hard reset.
https://github.com/git/git/blob/master/git-submodule.sh#L606
Once that is done we'd want to add a test case, presumably in t/t7406-submodule-update.sh
Quoted 17 lines
Quoted 9 lines
Quoted 6 lines
I would have asked for
git revert --recursive <commit>...
git rebase --recursive [-i] ...which I also frequently desire (could elaborate on the use cases etc).
These would be nice to have. It would be nice if you'd elaborate on the use cases for future reference in the mailing list archive. :-)
ok, will try to do so ;-) In summary: they are just a logical extension of git support for submodules for anyone actively working with submodules to keep entire tree in sync. Then quite often the need for reverting a specific commit (which also has changes reflected in submodules) arises. The same with rebase, especially to trim away some no longer desired changes reflected in submodules.
Quoted 4 lines
the initial "git submodule update --reset-hard" is pretty much a crude workaround for some of those cases, so I would just go earlier in the history, and redo some things, whenever I could just drop or revert some selected set of commits.
That makes sense.
Do you want to give the implementation a try for the --reset-hard switch?
Quoted 3 lines
ah... so it is only submodule command which has --recursive, and the
rest have --recurse-submodules when talking about recursing into
submodules?
I don't think we were that cautious in development as it was done by different people at different times. There is also just --submodule for the diff family, for reference:
https://public-inbox.org/git/[email protected]/
Quoted 7 lines · Stefan Beller
Quoted 4 lines
the initial "git submodule update --reset-hard" is pretty much a crude workaround for some of those cases, so I would just go earlier in the history, and redo some things, whenever I could just drop or revert some selected set of commits.
That makes sense.
Do you want to give the implementation a try for the --reset-hard switch?
ok, will do, thanks for the blessing ;-)
Signature · 5 lines
Yaroslav O. Halchenko Center for Open Neuroscience http://centerforopenneuroscience.org Dartmouth College, 419 Moore Hall, Hinman Box 6207, Hanover, NH 03755 Phone: +1 (603) 646-9834 Fax: +1 (603) 646-1419 WWW: http://www.linkedin.com/in/yarik
Quoted 10 lines · Yaroslav Halchenko
Quoted 7 lines · Stefan Beller
Quoted 4 lines
the initial "git submodule update --reset-hard" is pretty much a crude workaround for some of those cases, so I would just go earlier in the history, and redo some things, whenever I could just drop or revert some selected set of commits.
That makes sense.
Do you want to give the implementation a try for the --reset-hard switch?ok, will do, thanks for the blessing ;-)
The patch is attached (please advise if should be done differently) and also submitted as PR
https://github.com/git/git/pull/563
I guess it would need more tests. Took me some time to figure out why I was getting
fatal: bad value for update parameter
after all my changes to the git-submodule.sh script after looking at an example commit 42b491786260eb17d97ea9fb1c4b70075bca9523 which introduced --merge to the update ;-)
Signature · 5 lines
Yaroslav O. Halchenko Center for Open Neuroscience http://centerforopenneuroscience.org Dartmouth College, 419 Moore Hall, Hinman Box 6207, Hanover, NH 03755 Phone: +1 (603) 646-9834 Fax: +1 (603) 646-1419 WWW: http://www.linkedin.com/in/yarik
From 170296dc661b4bc3d942917ce27288df52ff650d Mon Sep 17 00:00:00 2001
From: Yaroslav Halchenko <[email protected]>
Date: Fri, 7 Dec 2018 21:28:29 -0500
Subject: [PATCH] submodule: Add --reset-hard option for git submodule update
This patch adds a --reset-hard option for the update command to hard reset submodule(s) to the gitlink for the corresponding submodule in the superproject. This feature is desired e.g. to be able to discard recent changes in the entire hierarchy of the submodules after running
git reset --hard PREVIOUS_STATE
in the superproject which leaves submodules in their original state, and
git reset --hard --recurse-submodules PREVIOUS_STATE
would result in the submodules being checked into detached HEADs.
As in the original git reset --hard no checks or any kind of safe-guards against jumping into some state which was never on the current branch is done.
must_die_on_failure is not set to yes to mimic behavior of a update --checkout strategy, which would leave user with a non-clean state immediately apparent via git status so an informed decision/actions could be done manually.
--- Documentation/git-submodule.txt | 12 +++++++++++- Documentation/gitmodules.txt | 4 ++-- builtin/submodule--helper.c | 3 ++- git-submodule.sh | 10 +++++++++- submodule.c | 4 ++++ submodule.h | 1 + t/t7406-submodule-update.sh | 17 ++++++++++++++++- 7 files changed, 45 insertions(+), 6 deletions(-)
Documentation/git-submodule.txt +11 -1
@@ -124,7 +124,7 @@ If you really want to remove a submodule from the repository and commit that use linkgit:git-rm[1] instead. See linkgit:gitsubmodules[7] for removal options. -update [--init] [--remote] [-N|--no-fetch] [--[no-]recommend-shallow] [-f|--force] [--checkout|--rebase|--merge] [--reference <repository>] [--depth <depth>] [--recursive] [--jobs <n>] [--] [<path>...]:: +update [--init] [--remote] [-N|--no-fetch] [--[no-]recommend-shallow] [-f|--force] [--checkout|--rebase|--merge|--reset-hard] [--reference <repository>] [--depth <depth>] [--recursive] [--jobs <n>] [--] [<path>...]:: + -- Update the registered submodules to match what the superproject
@@ -358,6 +358,16 @@ the submodule itself. If the key `submodule.$name.update` is set to `rebase`, this option is implicit. +--reset-hard:: + This option is only valid for the update command. + Hard reset current state to the commit recorded in the superproject. + If this option is given, the submodule's HEAD will not get detached + if it was not detached before. Note that, like with a regular + git reset --hard no safe-guards are in place to prevent jumping + to a commit which was never part of the current branch. + If the key `submodule.$name.update` is set to `reset-hard`, this + option is implicit. + --init:: This option is only valid for the update command. Initialize all submodules for which "git submodule init" has not been
Documentation/gitmodules.txt +2 -2
@@ -43,8 +43,8 @@ submodule.<name>.update:: command in the superproject. This is only used by `git submodule init` to initialize the configuration variable of the same name. Allowed values here are 'checkout', 'rebase', - 'merge' or 'none'. See description of 'update' command in - linkgit:git-submodule[1] for their meaning. Note that the + 'merge', 'reset-hard' or 'none'. See description of 'update' command + in linkgit:git-submodule[1] for their meaning. Note that the '!command' form is intentionally ignored here for security reasons.
builtin/submodule--helper.c +2 -1
@@ -1481,6 +1481,7 @@ static void determine_submodule_update_strategy(struct repository *r,if (just_cloned && (out->type ==SM_UPDATE_MERGE || out->type ==SM_UPDATE_REBASE || + out->type ==SM_UPDATE_RESET_HARD || out->type ==SM_UPDATE_NONE )) out->type =SM_UPDATE_CHECKOUT ;
@@ -1851,7 +1852,7 @@ static int update_clone(int argc, const char **argv, const char *prefix) "submodule boundaries")),OPT_STRING (0 ,"update" , &update,N_ ("string" ), -N_ ("rebase, merge, checkout or none" )), +N_ ("rebase, merge, checkout, reset-hard or none" )),OPT_STRING_LIST (0 ,"reference" , &suc.references,N_ ("repo" ),N_ ("reference repository" )), OPT_BOOL(0, "dissociate", &suc.dissociate,
git-submodule.sh +9 -1
@@ -9,7 +9,7 @@ USAGE="[--quiet] add [-b <branch>] [-f|--force] [--name <name>] [--reference <re or: $dashless [--quiet] status [--cached] [--recursive] [--] [<path>...] or: $dashless [--quiet] init [--] [<path>...] or: $dashless [--quiet] deinit [-f|--force] (--all| [--] <path>...) - or: $dashless [--quiet] update [--init] [--remote] [-N|--no-fetch] [-f|--force] [--checkout|--merge|--rebase] [--[no-]recommend-shallow] [--reference <repository>] [--recursive] [--] [<path>...] + or: $dashless [--quiet] update [--init] [--remote] [-N|--no-fetch] [-f|--force] [--checkout|--merge|--rebase|--reset-hard] [--[no-]recommend-shallow] [--reference <repository>] [--recursive] [--] [<path>...] or: $dashless [--quiet] summary [--cached|--files] [--summary-limit <n>] [commit] [--] [<path>...] or: $dashless [--quiet] foreach [--recursive] <command> or: $dashless [--quiet] sync [--recursive] [--] [<path>...]
@@ -483,6 +483,9 @@ cmd_update() -m|--merge) update="merge" ;; + --reset-hard) + update="reset-hard" + ;; --recursive) recursive=1 ;;
@@ -621,6 +624,11 @@ cmd_update() say_msg="$(eval_gettext "Submodule path '\$displaypath': merged in '\$sha1'")" must_die_on_failure=yes ;; + reset-hard) + command="git reset --hard" + die_msg="$(eval_gettext "Unable to reset --hard to '\$sha1' in submodule path '\$displaypath'")" + say_msg="$(eval_gettext "Submodule path '\$displaypath': was reset --hard to '\$sha1'")" + ;; !*) command="${update_module#!}" die_msg="$(eval_gettext "Execution of '\$command \$sha1' failed in submodule path '\$displaypath'")"
submodule.c +4 -0
@@ -373,6 +373,8 @@ enum submodule_update_type parse_submodule_update_type(const char *value)return SM_UPDATE_REBASE ; else if (!strcmp(value, "merge"))return SM_UPDATE_MERGE ; + else if (!strcmp(value, "reset-hard")) +return SM_UPDATE_RESET_HARD ; else if (*value == '!') return SM_UPDATE_COMMAND; else
@@ -406,6 +408,8 @@ const char *submodule_strategy_to_string(const struct submodule_update_strategyreturn "checkout" ;case SM_UPDATE_MERGE :return "merge" ; +case SM_UPDATE_RESET_HARD : +return "reset-hard" ;case SM_UPDATE_REBASE :return "rebase" ;case SM_UPDATE_NONE :
submodule.h +1 -0
@@ -29,6 +29,7 @@ enum submodule_update_type {SM_UPDATE_CHECKOUT ,SM_UPDATE_REBASE ,SM_UPDATE_MERGE , +SM_UPDATE_RESET_HARD ,SM_UPDATE_NONE , SM_UPDATE_COMMAND };
t/t7406-submodule-update.sh +16 -1
@@ -6,7 +6,8 @@ test_description='Test updating submodules This test verifies that "git submodule update" detaches the HEAD of the -submodule and "git submodule update --rebase/--merge" does not detach the HEAD. +submodule and "git submodule update --rebase/--merge/--reset-hard" does +not detach the HEAD. ' . ./test-lib.sh
@@ -305,6 +306,20 @@ test_expect_success 'submodule update --merge staying on master' ' ) ' +test_expect_success 'submodule update --reset-hard staying on master' ' + (cd super/submodule && + git reset --hard HEAD~1 + ) && + (cd super && + (cd submodule && + compare_head + ) && + git submodule update --reset-hard submodule && + cd submodule && + compare_head + ) +' + test_expect_success 'submodule update - rebase in .git/config' ' (cd super && git config submodule.submodule.update rebase
Signature · 1 line
2.20.0.rc2.8.g0a3bec4a1c.dirty
Quoted 11 lines · Yaroslav Halchenko
Quoted 5 lines · Yaroslav Halchenko
Quoted 4 lines · Stefan Beller
Quoted 4 lines
the initial "git submodule update --reset-hard" is pretty much a crude workaround for some of those cases, so I would just go earlier in the history, and redo some things, whenever I could just drop or revert some selected set of commits.
Quoted 8 lines
Quoted 4 lines
Quoted 2 lines
That makes sense.
Do you want to give the implementation a try for the --reset-hard switch?ok, will do, thanks for the blessing ;-)
The patch is attached (please advise if should be done differently) and also submitted as PR
https://github.com/git/git/pull/563
Yes, usually we send patches inline
(Random example:
https://public-inbox.org/git/244bdf2a6fc300f2b535ac8edfc2fbdaf5260266.1544465177.git.gitgitgadget@gmail.com/T/#u
compared to https://public-inbox.org/git/[email protected]/
(which I am replying to))
See Documentation/SubmittingPatches.
There are some tools that provide a GithubPR -> emailPatch workflow at
https://github.com/gitgitgadget/git
I think if you'd open your pull request there, then it would be automatically mailed to the list correctly.
I left some comments on the PR.
Quoted 2 lines
I guess it would need more tests.
Writing tests is hard, as we don't know what we expect to break. ;-)
Quoted 8 lines
Took me some time to figure out
why I was gettingfatal: bad value for update parameter
after all my changes to the
git-submodule.shscript after looking at an example commit42b491786260eb17d97ea9fb1c4b70075bca9523which introduced --merge to the update ;-)
Yeah I saw you also updated the submodule related C code, was that fatal message related to that?
Thanks,
Stefan
Quoted 9 lines
Quoted 9 lines
Took me some time to figure out
why I was gettingfatal: bad value for update parameter
Quoted 8 lines
Quoted 1 line
after all my changes to the
git-submodule.shscript after looking atan
Quoted 1 line
example commit
42b491786260eb17d97ea9fb1c4b70075bca9523whichintroduced
Quoted 1 line
--merge to the update ;-)
Yeah I saw you also updated the submodule related C code, was that fatal message related to that?
Yes
Signature · 3 lines
Yaroslav O. Halchenko (mobile version) Center for Open Neuroscience http://centerforopenneuroscience.org Dartmouth College, NH, USA
PATCH 1/2 submodule: Add --reset-hard option for git submodule update
In reply to Stefan BellerThis patch adds a --reset-hard option for the update command to hard reset submodule(s) to the gitlink for the corresponding submodule in the superproject. This feature is desired e.g. to be able to discard recent changes in the entire hierarchy of the submodules after running
git reset --hard PREVIOUS_STATE
in the superproject which leaves submodules in their original state, and
git reset --hard --recurse-submodules PREVIOUS_STATE
would result in the submodules being checked into detached HEADs.
As in the original git reset --hard no checks or any kind of safe-guards against jumping into some state which was never on the current branch is done.
must_die_on_failure is not set to yes to mimic behavior of a update --checkout strategy, which would leave user with a non-clean state immediately apparent via git status so an informed decision/actions could be done manually.
--- Documentation/git-submodule.txt | 12 +++++++++++- Documentation/gitmodules.txt | 4 ++-- builtin/submodule--helper.c | 3 ++- git-submodule.sh | 10 +++++++++- submodule.c | 4 ++++ submodule.h | 1 + t/t7406-submodule-update.sh | 17 ++++++++++++++++- 7 files changed, 45 insertions(+), 6 deletions(-)
Documentation/git-submodule.txt +11 -1
@@ -124,7 +124,7 @@ If you really want to remove a submodule from the repository and commit that use linkgit:git-rm[1] instead. See linkgit:gitsubmodules[7] for removal options. -update [--init] [--remote] [-N|--no-fetch] [--[no-]recommend-shallow] [-f|--force] [--checkout|--rebase|--merge] [--reference <repository>] [--depth <depth>] [--recursive] [--jobs <n>] [--] [<path>...]:: +update [--init] [--remote] [-N|--no-fetch] [--[no-]recommend-shallow] [-f|--force] [--checkout|--rebase|--merge|--reset-hard] [--reference <repository>] [--depth <depth>] [--recursive] [--jobs <n>] [--] [<path>...]:: + -- Update the registered submodules to match what the superproject
@@ -358,6 +358,16 @@ the submodule itself. If the key `submodule.$name.update` is set to `rebase`, this option is implicit. +--reset-hard:: + This option is only valid for the update command. + Hard reset current state to the commit recorded in the superproject. + If this option is given, the submodule's HEAD will not get detached + if it was not detached before. Note that, like with a regular + `git reset --hard` no safe-guards are in place to prevent jumping + to a commit which was never part of the current branch. + If the key `submodule.$name.update` is set to `reset-hard`, this + option is implicit. + --init:: This option is only valid for the update command. Initialize all submodules for which "git submodule init" has not been
Documentation/gitmodules.txt +2 -2
@@ -43,8 +43,8 @@ submodule.<name>.update:: command in the superproject. This is only used by `git submodule init` to initialize the configuration variable of the same name. Allowed values here are 'checkout', 'rebase', - 'merge' or 'none'. See description of 'update' command in - linkgit:git-submodule[1] for their meaning. Note that the + 'merge', 'reset-hard' or 'none'. See description of 'update' command + in linkgit:git-submodule[1] for their meaning. Note that the '!command' form is intentionally ignored here for security reasons.
builtin/submodule--helper.c +2 -1
@@ -1481,6 +1481,7 @@ static void determine_submodule_update_strategy(struct repository *r,if (just_cloned && (out->type ==SM_UPDATE_MERGE || out->type ==SM_UPDATE_REBASE || + out->type ==SM_UPDATE_RESET_HARD || out->type ==SM_UPDATE_NONE )) out->type =SM_UPDATE_CHECKOUT ;
@@ -1851,7 +1852,7 @@ static int update_clone(int argc, const char **argv, const char *prefix) "submodule boundaries")),OPT_STRING (0 ,"update" , &update,N_ ("string" ), -N_ ("rebase, merge, checkout or none" )), +N_ ("rebase, merge, checkout, reset-hard or none" )),OPT_STRING_LIST (0 ,"reference" , &suc.references,N_ ("repo" ),N_ ("reference repository" )), OPT_BOOL(0, "dissociate", &suc.dissociate,
git-submodule.sh +9 -1
@@ -9,7 +9,7 @@ USAGE="[--quiet] add [-b <branch>] [-f|--force] [--name <name>] [--reference <re or: $dashless [--quiet] status [--cached] [--recursive] [--] [<path>...] or: $dashless [--quiet] init [--] [<path>...] or: $dashless [--quiet] deinit [-f|--force] (--all| [--] <path>...) - or: $dashless [--quiet] update [--init] [--remote] [-N|--no-fetch] [-f|--force] [--checkout|--merge|--rebase] [--[no-]recommend-shallow] [--reference <repository>] [--recursive] [--] [<path>...] + or: $dashless [--quiet] update [--init] [--remote] [-N|--no-fetch] [-f|--force] [--checkout|--merge|--rebase|--reset-hard] [--[no-]recommend-shallow] [--reference <repository>] [--recursive] [--] [<path>...] or: $dashless [--quiet] summary [--cached|--files] [--summary-limit <n>] [commit] [--] [<path>...] or: $dashless [--quiet] foreach [--recursive] <command> or: $dashless [--quiet] sync [--recursive] [--] [<path>...]
@@ -483,6 +483,9 @@ cmd_update() -m|--merge) update="merge" ;; + --reset-hard) + update="reset-hard" + ;; --recursive) recursive=1 ;;
@@ -621,6 +624,11 @@ cmd_update() say_msg="$(eval_gettext "Submodule path '\$displaypath': merged in '\$sha1'")" must_die_on_failure=yes ;; + reset-hard) + command="git reset --hard" + die_msg="$(eval_gettext "Unable to reset --hard to '\$sha1' in submodule path '\$displaypath'")" + say_msg="$(eval_gettext "Submodule path '\$displaypath': was reset --hard to '\$sha1'")" + ;; !*) command="${update_module#!}" die_msg="$(eval_gettext "Execution of '\$command \$sha1' failed in submodule path '\$displaypath'")"
submodule.c +4 -0
@@ -373,6 +373,8 @@ enum submodule_update_type parse_submodule_update_type(const char *value)return SM_UPDATE_REBASE ; else if (!strcmp(value, "merge"))return SM_UPDATE_MERGE ; + else if (!strcmp(value, "reset-hard")) +return SM_UPDATE_RESET_HARD ; else if (*value == '!') return SM_UPDATE_COMMAND; else
@@ -406,6 +408,8 @@ const char *submodule_strategy_to_string(const struct submodule_update_strategyreturn "checkout" ;case SM_UPDATE_MERGE :return "merge" ; +case SM_UPDATE_RESET_HARD : +return "reset-hard" ;case SM_UPDATE_REBASE :return "rebase" ;case SM_UPDATE_NONE :
submodule.h +1 -0
@@ -29,6 +29,7 @@ enum submodule_update_type {SM_UPDATE_CHECKOUT ,SM_UPDATE_REBASE ,SM_UPDATE_MERGE , +SM_UPDATE_RESET_HARD ,SM_UPDATE_NONE , SM_UPDATE_COMMAND };
t/t7406-submodule-update.sh +16 -1
@@ -6,7 +6,8 @@ test_description='Test updating submodules This test verifies that "git submodule update" detaches the HEAD of the -submodule and "git submodule update --rebase/--merge" does not detach the HEAD. +submodule and "git submodule update --rebase/--merge/--reset-hard" does +not detach the HEAD. ' . ./test-lib.sh
@@ -305,6 +306,20 @@ test_expect_success 'submodule update --merge staying on master' ' ) ' +test_expect_success 'submodule update --reset-hard staying on master' ' + (cd super/submodule && + git reset --hard HEAD~1 + ) && + (cd super && + (cd submodule && + compare_head + ) && + git submodule update --reset-hard submodule && + cd submodule && + compare_head + ) +' + test_expect_success 'submodule update - rebase in .git/config' ' (cd super && git config submodule.submodule.update rebase
Signature · 1 line
2.20.0.rc2.8.g0a3bec4a1c.dirty
PATCH 2/2 RF+ENH(TST): compare the entire list of submodule status --recursive to stay intact
For submodule update --reset-hard the best test is comparison of the entire status as shown by submodule status --recursive. Upon update --reset-hard we should get back to the original state, with all the branches being the same (no detached HEAD) and commits identical to original (so no merges, new commits, etc).
For that, I have introduced two helpers: {record,compare}_submodules_status and an additional test for --reset-hard in nested submodule.
I have kept this as a separate PATCH to demonstrate the diff from the original test composition as introduced in the prior patch, and this one where all tests could be of the same type:
record_submodule_status &&
perform evil actions &&
! compare_submodule_status && # to double check that evil was done
git submodule --reset-hard . &&
compare_submodule_status # assure that we are all good--- t/t7406-submodule-update.sh | 37 ++++++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-)
t/t7406-submodule-update.sh +30 -7
@@ -21,6 +21,17 @@ compare_head() test "$sha_master" = "$sha_head" } +record_submodules_status() +{ + git submodule status --recursive >expect +} + +compare_submodules_status() +{ + git submodule status --recursive >actual && + test_i18ncmp expect actual +} + test_expect_success 'setup a submodule tree' ' echo file > file &&
@@ -294,7 +305,7 @@ test_expect_success 'submodule update --rebase staying on master' ' test_expect_success 'submodule update --merge staying on master' ' (cd super/submodule && - git reset --hard HEAD~1 + git reset --hard HEAD~1 ) && (cd super && (cd submodule &&
@@ -307,16 +318,28 @@ test_expect_success 'submodule update --merge staying on master' ' ' test_expect_success 'submodule update --reset-hard staying on master' ' - (cd super/submodule && - git reset --hard HEAD~1 - ) && (cd super && + record_submodules_status && (cd submodule && - compare_head + git reset --hard HEAD~1 ) && + ! compare_submodules_status && git submodule update --reset-hard submodule && - cd submodule && - compare_head + compare_submodules_status + ) +' + +test_expect_success 'submodule update --reset-hard in nested submodule' ' + (cd recursivesuper && + git submodule update --init --recursive && + record_submodules_status && + (cd super/submodule && + echo 123 >> file && + git commit -m "new commit" file + ) && + ! compare_submodules_status && + git submodule update --reset-hard --recursive && + compare_submodules_status ) '
Signature · 1 line
2.20.0.rc2.8.g0a3bec4a1c.dirty
Re: [PATCH 2/2] RF+ENH(TST): compare the entire list of submodule status --recursive to stay intact
On Mon, Dec 10, 2018 at 8:09 PM Yaroslav Halchenko <[email protected]> wrote:
Thanks for the patches. The first patch looks good to me!
Quoted 1 line
[PATCH 2/2] RF+ENH(TST): compare the entire list of submodule status --recursive to stay intact
The subject is a bit cryptic (specifically the first part before the colon), maybe
t7406: compare entire submodule status for --reset-hard mode
?
Quoted 5 lines
For submodule update --reset-hard the best test is comparison of the entire status as shown by submodule status --recursive. Upon update --reset-hard we should get back to the original state, with all the branches being the same (no detached HEAD) and commits identical to original (so no merges, new commits, etc).
"original state" can mean different things to different people. I'd think we could be more precise:
... we should get to the state that the submodule is reset to the
object id as the superprojects gitlink points at, irrespective of the submodule branch.
Review
Quoted 4 lines
test_expect_success'submodule update --merge staying on master' '(cd super/submodule &&(quoted diff) +1
-1- git reset --hard HEAD~1 + git reset --hard HEAD~1
unrelated white space change?
t/t7406-submodule-update.sh test_expect_success 'submodule update --merge staying on master' ' · line 331
Quoted 10 lines
) && (cd super && (cd submodule &&t/t7406-submodule-update.sh +2
-0@@ -307,16 +318,28 @@ test_expect_success 'submodule update --merge staying on master' ' ' test_expect_success 'submodule update --reset-hard staying on master' ' +' +
The tests look good to me, though I wonder if we'd rather want to inline {record/compare}_submodule_status as then you'd not need to look it up and the functions are rather short?
Re: [PATCH 2/2] RF+ENH(TST): compare the entire list of submodule status --recursive to stay intact
Thank you Stefan for the review and please pardon my delay with the reply, and sorry it got a bit too long by the end ;)
Quoted 1 line · Stefan Beller
Thanks for the patches. The first patch looks good to me!
Great!
Quoted 16 lines
Quoted 1 line
[PATCH 2/2] RF+ENH(TST): compare the entire list of submodule status --recursive to stay intact
The subject is a bit cryptic (specifically the first part before the colon), maybe
t7406: compare entire submodule status for --reset-hard mode
?
Quoted 5 lines
For submodule update --reset-hard the best test is comparison of the entire status as shown by submodule status --recursive. Upon update --reset-hard we should get back to the original state, with all the branches being the same (no detached HEAD) and commits identical to original (so no merges, new commits, etc).
Quoted 6 lines
"original state" can mean different things to different people. I'd think we could be more precise:
... we should get to the state that the submodule is reset to the
object id as the superprojects gitlink points at, irrespective of the submodule branch.
ok, I will update the description. But I wonder if there could be some short term to be used to describe the composite "git submodule status" and "git status" (refers to below ;)).
Quoted 6 lines
Review
Quoted 4 lines
test_expect_success'submodule update --merge staying on master' '(cd super/submodule &&(quoted diff) +1
-1- git reset --hard HEAD~1 + git reset --hard HEAD~1unrelated white space change?
I was tuning formatting to be uniform and I guess missed that this is in the other (not my) test. I will revert that piece, thanks!
BTW -- should I just squash to PATCHes now? I kept them separate primarily to show the use of those helpers:
Quoted 6 lines
Quoted 5 lines
) && (cd super && (cd submodule &&(quoted diff) +0
-0@@ -307,16 +318,28 @@ test_expect_success 'submodule update --merge staying on master' ' '
Quoted 8 lines
t/t7406-submodule-update.sh test_expect_success 'submodule update --merge staying on master' ' · line 331
Quoted 4 lines
t/t7406-submodule-update.sh +2
-0test_expect_success 'submodule update --reset-hard staying on master' ' +' +The tests look good to me, though I wonder if we'd rather want to inline {record/compare}_submodule_status as then you'd not need to look it up and the functions are rather short?
compare_submodules_status is already a compound action, so code would become quite more "loaded" if it is expanded, e.g. instead of
(cd super && record_submodules_status && (cd submodule && git reset --hard HEAD~1 ) && ! compare_submodules_status && git submodule update --reset-hard submodule && compare_submodules_status )
it would become something like this I guess?
(cd super &&
git submodule status --recursive >expect &&
(cd submodule &&
git reset --hard HEAD~1
) &&
! {git submodule status --recursive >actual &&
test_i18ncmp expect actual;} &&
git submodule update --reset-hard submodule &&
{git submodule status --recursive >actual &&
test_i18ncmp expect actual;}
)IMHO a bit mouth full. I was thinking also to extend compare_ with additional testing e.g. using "git status" since "git submodule status" does not care about untracked files etc. For --reset-hard I would like to assure that it is not just some kind of a mixed reset leaving files behind. That would make tests even more overloaded.
On that point: Although I also like explicit calls at times, I also do like test fixtures as a concept to do more testing around the actual test-specific code block, thus minimizing boiler plate, which even if explicit makes code actually harder to grasp (at least to me).
Since for the majority of the --reset-hard tests the fixture and test(s) are pretty much the same, actually ideally I would have liked to have something like this:
test_expect_unchanged_submodule_status 'submodule update --reset-hard staying on master' \
super \
'(cd submodule && git reset --hard HEAD~1)' \
'git submodule update --reset-hard submodule'
where I just pass
the path to work in,
the test setup function,
and the test action.
The rest (initial cd, record, run setup, verify that there is a change, run action, verify there is no changes) is done by the test_expect_unchanged_submodule_status in a uniform way, absorbing all the boiler plate. (I am not married to the name, could be more descriptive/generic may be)
Then we could breed a good number of tests with little to no boiler plate, with only relevant pieces and as extended as needed testing done by this test_expect_unchanged_submodule_status helper. e.g smth like
test_expect_unchanged_submodule_status 'submodule update --reset-hard staying on master when I do a new commit' \
super \
'(cd submodule && git commit --allow-empty -m "new one"' \
'git submodule update --reset-hard submodule'
and kaboom -- we have a new test. If we decide to test more -- just tune up test_expect_unchanged_submodule_status and done -- all the tests remain sufficiently prescribed.
What do you think?
Signature · 5 lines
Yaroslav O. Halchenko Center for Open Neuroscience http://centerforopenneuroscience.org Dartmouth College, 419 Moore Hall, Hinman Box 6207, Hanover, NH 03755 Phone: +1 (603) 646-9834 Fax: +1 (603) 646-1419 WWW: http://www.linkedin.com/in/yarik
Re: [PATCH 2/2] RF+ENH(TST): compare the entire list of submodule status --recursive to stay intact
Quoted 41 lines · Yaroslav O Halchenko
Thank you Stefan for the review and please pardon my delay with the reply, and sorry it got a bit too long by the end ;)
Quoted 1 line · Stefan Beller
Thanks for the patches. The first patch looks good to me!
Great!
Quoted 16 lines
Quoted 1 line
[PATCH 2/2] RF+ENH(TST): compare the entire list of submodule status --recursive to stay intact
The subject is a bit cryptic (specifically the first part before the colon), maybe
t7406: compare entire submodule status for --reset-hard mode
?
Quoted 5 lines
For submodule update --reset-hard the best test is comparison of the entire status as shown by submodule status --recursive. Upon update --reset-hard we should get back to the original state, with all the branches being the same (no detached HEAD) and commits identical to original (so no merges, new commits, etc).
Quoted 6 lines
"original state" can mean different things to different people. I'd think we could be more precise:
... we should get to the state that the submodule is reset to the
object id as the superprojects gitlink points at, irrespective of the submodule branch.ok, I will update the description. But I wonder if there could be some short term to be used to describe the composite "git submodule status" and "git status" (refers to below ;)).
Quoted 4 lines
Quoted 4 lines
test_expect_success'submodule update --merge staying on master' '(cd super/submodule &&(quoted diff) +1
-1- git reset --hard HEAD~1 + git reset --hard HEAD~1
Quoted 4 lines
Quoted 1 line
unrelated white space change?
I was tuning formatting to be uniform and I guess missed that this is in the other (not my) test. I will revert that piece, thanks!
The tests in that file are not quite following the coding style that is currently deemed the best. So if you want to clean that up as a preparatory patch, feel welcome to do so. :-)
(c.f. t/t7400-submodule-basic.sh for good style, specifically indentation by tabs and the cd <path> on its own line in
a subshell)
The latest style update I found is80938c39e2 (pack-objects test: modernize style, 2018-10-30) and submodule related test style31158c7efc (t7410: update to new style, 2018-08-15)
So I was not opposed to have style changes, but to have multiple unrelated things in one patch (feature work vs cleanup).
Quoted 2 lines
BTW -- should I just squash to PATCHes now? I kept them separate primarily to show the use of those helpers:
That would make sense.
Quoted 2 lines
compare_submodules_statusis already a compound action, so code would become quite more "loaded" if it is expanded, e.g. instead of
...
Quoted 1 line
it would become something like this I guess?
...
Quoted 1 line
! {git submodule status --recursive >actual &&you could keep the status out of the negation.
Quoted 11 lines
test_i18ncmp expect actual;} && git submodule update --reset-hard submodule && {git submodule status --recursive >actual && test_i18ncmp expect actual;} )IMHO a bit mouth full. I was thinking also to extend
compare_with additional testing e.g. using "git status" since "git submodule status" does not care about untracked files etc. For --reset-hard I would like to assure that it is not just some kind of a mixed reset leaving files behind. That would make tests even more overloaded.
ok, that makes sense.
Quoted 19 lines
On that point: Although I also like explicit calls at times, I also do like test fixtures as a concept to do more testing around the actual test-specific code block, thus minimizing boiler plate, which even if explicit makes code actually harder to grasp (at least to me).
Since for the majority of the --reset-hard tests the fixture and test(s) are pretty much the same, actually ideally I would have liked to have something like this:
test_expect_unchanged_submodule_status'submodule update --reset-hard staying on master' \
super \
'(cd submodule && git reset --hard HEAD~1)' \
'git submodule update --reset-hard submodule'where I just pass
the path to work in,
the test setup function,
and the test action.
Quoted 5 lines
The rest (initial cd, record, run setup, verify that there is a change, run action, verify there is no changes) is done by the
test_expect_unchanged_submodule_statusin a uniform way, absorbing all the boiler plate. (I am not married to the name, could be more descriptive/generic may be)
The issue with submodules is that we're already deviating from the 'standard' git test suite at times (See the submodule test suite lib-submodule-update.sh that is used via t1013, t2013 or t3906 and others).
I guess if we keep the test_expect_unchanged_submodule_status as a file local function, it could be okay.
Quoted 7 lines
Then we could breed a good number of tests with little to no boiler plate, with only relevant pieces and as extended as needed testing done by this
test_expect_unchanged_submodule_statushelper. e.g smth like
test_expect_unchanged_submodule_status'submodule update --reset-hard staying on master when I do a new commit' \
super \
'(cd submodule && git commit --allow-empty -m "new one"' \
In new tests we're a big fan of using -C, as that can save the subshell, i.e. replace the whole line by
git -C submodule commit --allow-empty -m "new one"' &&
Quoted 7 lines
'git submodule update --reset-hard submodule'and kaboom -- we have a new test. If we decide to test more -- just tune up
test_expect_unchanged_submodule_statusand done -- all the tests remain sufficiently prescribed.What do you think?
That is pretty cool. Maybe my gut reaction on the previous patch also had to do with the numbers, i.e. having 2 extra function for only having 2 tests more legible. A framework is definitely better once we have more tests.
Stefan
Re: [PATCH 2/2] RF+ENH(TST): compare the entire list of submodule status --recursive to stay intact
Quoted 10 lines · Stefan Beller
Quoted 5 lines
and kaboom -- we have a new test. If we decide to test more -- just tune up
test_expect_unchanged_submodule_statusand done -- all the tests remain sufficiently prescribed.What do you think?
That is pretty cool. Maybe my gut reaction on the previous patch also had to do with the numbers, i.e. having 2 extra function for only having 2 tests more legible. A framework is definitely better once we have more tests.
cool, thanks for the feedback - I will then try to make it happen
quick one (so when I get to it I know): should I replicate all those tests you have for other update strategies? (treating of config specifications etc) There is no easy way to parametrize them somehow?
;) In Python world I might have mocked the actual underlying call to
update, to see what option it would be getting and assure that it is the one I specified via config, and then sweepped through all of them to make sure nothing interim changes it. Just wondering if may be something like that exists in git's tests support.
BTW - sorry if RTFM and unrelated, is there a way to
update --merge
but allowing only fast-forwards? My use case is collection of this
submodules: http://datasets.datalad.org/?dir=/openneuro which all should come from github and I should not have any changes of my own. Sure thing if all is clean etc, merge should result in fast-forward. I just do not want to miss a case where there was some (temporary?) "dirt" which I forgot to reset and it would then get merged etc.
Signature · 5 lines
Yaroslav O. Halchenko Center for Open Neuroscience http://centerforopenneuroscience.org Dartmouth College, 419 Moore Hall, Hinman Box 6207, Hanover, NH 03755 Phone: +1 (603) 646-9834 Fax: +1 (603) 646-1419 WWW: http://www.linkedin.com/in/yarik
Re: [PATCH 2/2] RF+ENH(TST): compare the entire list of submodule status --recursive to stay intact
Quoted 15 lines · Yaroslav O Halchenko
Quoted 10 lines · Stefan Beller
Quoted 5 lines
and kaboom -- we have a new test. If we decide to test more -- just tune up
test_expect_unchanged_submodule_statusand done -- all the tests remain sufficiently prescribed.What do you think?
That is pretty cool. Maybe my gut reaction on the previous patch also had to do with the numbers, i.e. having 2 extra function for only having 2 tests more legible. A framework is definitely better once we have more tests.
Quoted 5 lines
cool, thanks for the feedback - I will then try to make it happen
quick one (so when I get to it I know): should I replicate all those tests you have for other update strategies? (treating of config specifications etc)
If there is a sensible way to do so?
I have the impression that there are enough differences, that it may not be possible to replicate all tests meaningfully from the other modes.
Quoted 1 line
There is no easy way to parametrize them somehow?
There is t/lib-submodule-update.sh, which brings this to an extreme, as it makes a "test suite in a test suite"; and I would not follow that example for this change.
Quoted 5 lines
;) In Python world I might have mocked the actual underlying call to
update, to see what option it would be getting and assure that it is the one I specified via config, and then sweepped through all of them to make sure nothing interim changes it. Just wondering if may be something like that exists in git's tests support.
gits tests are very heavy on end to end testing, i.e. run a whole command and observe its output. This makes our command setup code, (i.e. finding the repository, parsing options, reading possible config, etc) a really well exercised code path. ;-)
There is a recent push towards testing only units, most of t/helper is used for that, e.g. c.f. 4c7bb45269 (test-reach:
test get_reachable_subset, 2018-11-02).
So if you have a good idea how to focus the submodule tests more on the (new) unit that you add, that would be cool.
Quoted 7 lines
BTW - sorry if RTFM and unrelated, is there a way to
update --mergebut allowing only fast-forwards? My use case is collection of this
submodules: http://datasets.datalad.org/?dir=/openneuro which all should come from github and I should not have any changes of my own.
So you want the merge option --ff-only
to be passed to the submodule merge command. I guess you could make a patch, that update takes another option (--ff-only, only useful when --merge is given), which is then propagated.
I am not sure if we could have a more generalized option passing, which would allow to pass any option (for its respective command) to the command that is run in the update mode.
Quoted 3 lines
Sure thing if all is clean etc, merge should result in fast-forward. I just do not want to miss a case where there was some (temporary?) "dirt" which I forgot to reset and it would then get merged etc.
maybe use --rebase, such that your potential change would bubble up and possibly produce a merge conflict?
Re: [PATCH 2/2] RF+ENH(TST): compare the entire list of submodule status --recursive to stay intact
Quoted 10 lines · Stefan Beller
Quoted 5 lines
cool, thanks for the feedback - I will then try to make it happen
quick one (so when I get to it I know): should I replicate all those tests you have for other update strategies? (treating of config specifications etc)
If there is a sensible way to do so?
I have the impression that there are enough differences, that it may not be possible to replicate all tests meaningfully from the other modes.
oh, by replicate I just meant to copy/paste and adjust for expected for --reset-hard test behavior (and possibly introduced helper), nothing fancy, just duplication as for replication ;-)
Quoted 5 lines
Quoted 1 line
There is no easy way to parametrize them somehow?
There is
t/lib-submodule-update.sh, which brings this to an extreme, as it makes a "test suite in a test suite"; and I would not follow that example for this change.
ok
Quoted 11 lines
Quoted 5 lines
;) In Python world I might have mocked the actual underlying call to
update, to see what option it would be getting and assure that it is the one I specified via config, and then sweepped through all of them to make sure nothing interim changes it. Just wondering if may be something like that exists in git's tests support.gits tests are very heavy on end to end testing, i.e. run a whole command and observe its output. This makes our command setup code, (i.e. finding the repository, parsing options, reading possible config, etc) a really well exercised code path. ;-)
Quoted 6 lines
There is a recent push towards testing only units, most of t/helper is used for that, e.g. c.f.
4c7bb45269(test-reach:
testget_reachable_subset, 2018-11-02).So if you have a good idea how to focus the submodule tests more on the (new) unit that you add, that would be cool.
no, not really any good ideas -- I am new here, but I will keep an eye open.
Quoted 8 lines
Quoted 7 lines
BTW - sorry if RTFM and unrelated, is there a way to
update --mergebut allowing only fast-forwards? My use case is collection of this
submodules: http://datasets.datalad.org/?dir=/openneuro which all should come from github and I should not have any changes of my own.
Quoted 8 lines
So you want the merge option --ff-only
to be passed to the submodule merge command. I guess you could make a patch, that update takes another option (--ff-only, only useful when --merge is given), which is then propagated.I am not sure if we could have a more generalized option passing, which would allow to pass any option (for its respective command) to the command that is run in the update mode.
wouldn't it be (theoretically) possible, in principle, to pass them via some config variable? e.g. instead of
submodule update --reset-hard
have
-c submodule.update.reset.opts=--hard update --reset
and then analogously
-c submodule.update.merge.opts=--ff-only update --merge
(--ff-only I guess would make no sense for any "supermodule" - a repo with submodules)
Quoted 6 lines
Quoted 3 lines
Sure thing if all is clean etc, merge should result in fast-forward. I just do not want to miss a case where there was some (temporary?) "dirt" which I forgot to reset and it would then get merged etc.
maybe use --rebase, such that your potential change would bubble up and possibly produce a merge conflict?
that is a good idea as a workaround, thanks!
Signature · 5 lines
Yaroslav O. Halchenko Center for Open Neuroscience http://centerforopenneuroscience.org Dartmouth College, 419 Moore Hall, Hinman Box 6207, Hanover, NH 03755 Phone: +1 (603) 646-9834 Fax: +1 (603) 646-1419 WWW: http://www.linkedin.com/in/yarik