10

I am using git subtree to organize my git repositories. Let's say I have a main repository called repo and a library called lib.

I successfully "imported" the lib repository by squashing its history. I would now like to contribute back to lib by squashing the history too. This does not seem to work: I specify the --squash option to git subtree push but when looking at the history I still send all the commits.

How to reproduce

Here is a script showing the minimal commands needed to reproduce the problem:

#!/bin/bash
rm -rf lib lib-work repo

# repo is the main repository
git init repo
# lib is the 'subtreed' repository (bare to accept pushes)
git init --bare lib

git clone lib lib-work
cd lib-work
# adding a bunch of commits to lib
echo "v1" > README
git add README
git commit -m 'lib commit 1'
echo "v2" > README
git add README
git commit -m 'lib commit 2'
echo "v3" > README
git add README
git commit -m 'lib commit 3'
git push origin master
cd ..

cd repo
# adding initial commit to have a valid HEAD
echo "v1" > README
git add README
git commit -m 'repo commit 1'
git remote add lib ../lib
git subtree add --prefix lib lib master --squash
echo "v4" > lib/README
git add lib/README
git commit -m 'repo commit 2'
echo "v5" > lib/README
git add lib/README
git commit -m 'repo commit 3'
echo "v6" > lib/README
git add lib/README
git commit -m 'repo commit 4'
#git log --all --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s%Creset' --abbrev-commit
# "not working" command :
git subtree push --prefix lib lib master --squash

# pretty print the history
git log --all --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s%Creset' --abbrev-commit
cd ../lib
echo
git log --all --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s%Creset' --abbrev-commit

git log showing the problem

The output of the two git log blabla commands are:

* b075d5e - (HEAD, master) repo commit 4
* ebdc7c7 - repo commit 3
* 9f1edab - repo commit 2
*   3d48bca - Merge commit '34e16a547819da7e228f3add35efe86197d2ddcb' as 'lib'
|\
| * 34e16a5 - Squashed 'lib/' content from commit 2643625
* 3f1490c - repo commit 1
* 1f86fe3 - (lib/master) repo commit 4
* 9f1639a - repo commit 3
* 8bd01bd - repo commit 2
* 2643625 - lib commit 3
* 3d64b8c - lib commit 2
* aba9fcb - lib commit 1

and :

* 1f86fe3 - (HEAD, master) repo commit 4
* 9f1639a - repo commit 3
* 8bd01bd - repo commit 2
* 2643625 - lib commit 3
* 3d64b8c - lib commit 2
* aba9fcb - lib commit 1

As you can see, lib sees the "repo commit 2,3,4" although I specified the squash option. The other way around worked hence the Squashed 'lib/' content from commit f28bf8e.

I tried on windows with git version 1.8.1.msysgit.1 and on linux with git version 1.8.3.4.

So why doesn't the --squash option do a squash?

Side question

Why does lib/master appears in the log of the repo repository ? Knowing it appears only after the "failed" git push: if you uncomment the first git log blabla you get the following output showing the stashed history but no sign of lib/master :

* b075d5e - (HEAD, master) repo commit 4
* ebdc7c7 - repo commit 3
* 9f1edab - repo commit 2
*   3d48bca - Merge commit '34e16a547819da7e228f3add35efe86197d2ddcb' as 'lib'
|\
| * 34e16a5 - Squashed 'lib/' content from commit 2643625
* 3f1490c - repo commit 1
ibizaman
  • 3,053
  • 1
  • 23
  • 34
  • For the side question, you are seeing the extra logs because of the `--all` option. Using that option acts as if you are getting the logs of all the references in `refs/`, since the push operation creates a new reference there it is thus printed along the logs of `HEAD`. – Maic López Sáenz Nov 22 '13 at 07:33
  • I think the logs you have in the side question are wrong, or perhaps of a different time you created your repositories, since the hashes do not match with the ones you have above. – Maic López Sáenz Nov 22 '13 at 07:34
  • @LopSae, Thanks :) Is there a way to prevent push to add a new refence? If not, why doesn't the fetch need to add a reference ? About the side question, sorry for the confusion, I indeed did the test later on, that's why the commits are not the same. I updated the question. – ibizaman Nov 22 '13 at 07:53
  • 1
    By using `git subtree push` you are doing a `split` that creates a branch (the extra reference) and then pushing that reference. It is possible to use `split` on its own and it will just create a commit without creating the new branch, `split` will just print the created commit hash. – Maic López Sáenz Nov 22 '13 at 07:59
  • I see, it doesn't mind me that push creates that new branch, I just don't want to see it. It seems replacing `--all` by `--branches` does the trick, [link to log manual](https://www.kernel.org/pub/software/scm/git/docs/git-log.html). – ibizaman Nov 22 '13 at 08:24
  • Try squash in an interactive rebase as explained here http://stackoverflow.com/questions/6934752/combining-multiple-commits-before-pushing-in-git – Jorge Orpinel Pérez Nov 19 '14 at 20:41
  • Note: Git 2.5+ (Q2 2015) will fix the `git subtree` documentation: see [my answer below](http://stackoverflow.com/a/30441980/6309) – VonC May 25 '15 at 15:56

3 Answers3

9

It is possible that this is an error in the documentation of the subtree command.

The manual in git states:

options for 'add', 'merge', 'pull' and 'push'
    --squash              merge subtree changes as a single commit

If you check the more extended documentation in the original subtree project you will notice that the --squash option is only explained for add and merge, as the functionality is described for the process of bringing content into your repository. Since pull is a modified form of merge, it is also implied that it can use --squash.

The push in the manual list what does not make sense. The git subtree push subcommand is a combination of git subtree split and git push. This means that --squash should be an option also supported by split, but split is not listed in the manual list. It is neither ever stated in the documentation that it can use --squash.

The --squash option is indeed accepted by split and push without error, but after experiment with it it seems it makes no difference, just as your example states. My take is that it is there by mistake and just ignored by the split and push commands.

Maic López Sáenz
  • 10,385
  • 4
  • 44
  • 57
  • That seems to be it: when digging a little in the [subtree file](https://github.com/git/git/blob/master/contrib/subtree/git-subtree.sh), you can see the squash options used in `cmd_add_commit()` and `cmd_merge()` but not in `cmd_split()`. – ibizaman Nov 22 '13 at 08:07
  • 2
    Update: I have sent a patch to the git mailing list fixing this. Hope to hear from them soon :) – ibizaman Nov 25 '13 at 16:47
5

This is now (Git 2.5, Q2 2015) confirmed in the official git subtree documentation.

See commit 6ccc71a by Danny Lin (danny0838), 07 May 2015.
(Merged by Junio C Hamano -- gitster -- in commit 6263f58, 22 May 2015)

contrib/subtree: there's no push --squash

The documentation says that --squash is for 'add', 'merge', 'pull' and 'push', while --squash actually doesn't change the behavior of 'push'.
Correct the documentation.

--squash:

This option is only valid for add, merge, and pull commands.


With Git 2.29 (Q4 2020), the documentation for subtree (in contrib/) is clearer.

See commit ce820cb, commit f99c0c9 (18 Aug 2020) by Danny Lin (danny0838).
(Merged by Junio C Hamano -- gitster -- in commit 8923a45, 24 Aug 2020)

contrib/subtree: document 'push' does not take '--squash'

Signed-off-by: Danny Lin

git subtree push does not support --squash, as previously illustrated in 6ccc71a9 ("contrib/subtree: there's no push --squash", 2015-05-07, Git v2.5.0-rc0 -- merge listed in batch #4)

The documentation no longer mentions push.

VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
0

I have found this thread because I just had the problem that the subtree push sends all the commits of the entire history of the parent repo and not just the ones regarding the newly added subtree. It turned out that the subtree cache was somehow corrupted due to a botched previous subtree add operation. I had to delete the .git\subtree-cache directory to fix the issue.

Mani
  • 11
  • 1