Home > database >  Can I clone all commits from a git repository at once?
Can I clone all commits from a git repository at once?

Time:07-20

Is there a git command equivalent to git clone, only instead of cloning the most recent version of a repository, it clones each commit as if it were a separate repository in a specific directory?

multiple commits cloning

CodePudding user response:

A clone of the repository contains all the commits in that repository. If you want to check out a revision, you can use git checkout REVISION to check out any version you like.

If you want to create a separate directory that is detached from the repository that contains the contents at a revision, you can use git archive --format=tar REVISION | tar -C OTHER-DIR -xf - That will output the contents of that revision into OTHER-DIR. Note that doing this for many commits is extremely wasteful of disk space, since Git stores the entire repository in a highly compressed format.

If you wanted to do this with all revisions, you could do something like this, passing the main directory on the command line:

#!/bin/sh

export DESTDIR="$0"

git rev-list --all | \
  xargs -L1 sh -c 'mkdir "$DESTDIR/$0" && git archive --format=tar $0 | \
  tar -C "$DESTDIR/$0" -xf -'

However, as mentioned, there is usually little reason to do this.

CodePudding user response:

No, there is no command to do this. What you're after is highly unusual and a little bit suspicious.

I'm assuming you want copy for commits for a single branch, not for all branches in your repo.

You could achieve this with a script:

  1. Find list of commits. For all commits in a branch git rev-list branch_name (optionally --max-count= and/or --since=, etc.)
  2. Then for each commit
    • make a clone of the repo using --single-branch
    • git checkout the_commit
    • UPDATE. git archive is much better for this.

The script could look like this

for commit in `git rev-list main --max-count=some_number`; do \
mkdir ../repo-$commit; \
git archive --format tar $commit | tar -xf - -C ../repo-$commit; \
done

CodePudding user response:

As far as I'm aware, there's no native way to handle this in Git.

However, you can write a script to handle this once you clone the repo and navigate into it:

#!/bin/bash

DEST_DIR="$HOME/test_dir" # Destination dir. Change this for a different location to store each commit.

START_BRANCH=$(git rev-parse --abbrev-ref HEAD)   # The branch we're currently on so we can reset our state at the end of the operations.
# GIT_TOPLEVEL=$(git rev-parse --show-toplevel)   # toplevel git dir, but in git bash on Windows this prints a windows path.
GIT_TOPLEVEL=$(cygpath.exe -u $(git rev-parse --show-toplevel));    # Pass the windows path produced by rev-parse to cygpath to convert to UNIX for git bash

echo "Printing toplevel: $GIT_TOPLEVEL";

cd $GIT_TOPLEVEL;
for sha1 in $(git log --no-merges --format="%H"); do      # For each commit in the log (excluding merges)
    echo "Checking out: $sha1"
    git checkout $sha1 2> /dev/null 1>&2;         # Detached checkout
    echo "Checkout $sha1 done"
    mkdir -p "$DEST_DIR/$sha1";           # Make a directory of the latest hash
    echo "Dir created!"
    git diff --name-only HEAD^ HEAD \
        | xargs -I {} cp -a {} $DEST_DIR/$sha1/;            # copy only files changed in the specific commit
    echo "copying...."
done;

git checkout --force $START_BRANCH 2> /dev/null 1>&2;    # reset our branch to the one we were on at the start of all this

I tested this in a copy of the git project's repo and it works using Git Bash on Windows. With that said, it could be much cleaner, and it isn't very performant, especially if you have a lot of commits. Also, it's currently set up to walk the entire history, if you want to grab a subsection of the history, you can pass a range to git log. For example, if I want the last two commits, I would do git log --no-merges --format="%H" HEAD~2..HEAD in the for loop.

This is just something I made in an hour or so. This also will copy only the files changed in each commit. For a copy of the whole repo, see the suggested change below, which replaces the git diff | xargs cp line in the for loop above.

    cp -ar $GIT_TOPLEVEL $DEST_DIR/$sha1/;            # copy all files
  • Related