0

The question is similar to this one: How to list all the files in a commit?

, however it is a bit different. What I need — is to get a list of files, affected (added/changed/deleted) by the latest git pull command.

It can be one commit (in which case I could get away with git show --name-only --oneline HEAD, but it can as well be several — and this is the tricky part.

Practical example: a deployment script like this:

git stash
git pull origin production

# ... some other stuff ...

assets=$(git show --name-only --oneline ??LAST_GIT_PULL_RANGE?? | fgrep 'resource/assets' | wc -l)
test $assets -gt 0 && npm run production

(compiles js/css resources only if there were changes in resource/assets directory)

pilat
  • 1,094
  • 1
  • 10
  • 17
  • Question was already [asked](https://stackoverflow.com/q/8522619/1705829), possible duplicate. – Timo Apr 13 '23 at 09:54

2 Answers2

2

This is a little bit difficult for several reasons:

  1. git pull means run git fetch, then run a second Git command to incorporate whatever got fetched.
  2. Git does not record here which second command is used. The second command is chosen by whoever ran git pull, so you would have to make sure that whoever ran it, remembers which command they chose. (The choice is based on both command line options and Git configuration settings.)
  3. The command you need to use to obtain the right answer depends on both what came in and what second command was used.

Practical example: a deployment script ...

Deployment scripts should never use git pull, so this gives you an easy way out of the problem: just don't use git pull in the deployment script. Run git fetch, then run your own second Git command. Now you know for sure which second command was used, because your script, not the user running your script, chose that second command.

Now, let's assume the second command, the one that you choose in your script, is git merge. There is still a problem here because git merge can do a fast-forward merge, but it can do a real merge. If it does a real merge, the merge can fail. If you tell it do only a fast-forward merge (using git merge --ff-only), that also can fail, if a real merge is required.

There is no single correct action for all cases here. You must decide what you want to have happen based on your situations. But let's assume, for the moment, that git merge --ff-only along with a test of the form "if the merge fails, abort the deployment entirely" is sufficient. Then your script would contain these lines:

git fetch origin || die ...
starthash=$(git rev-parse HEAD) || die ...
git merge --ff-only || die ...

where die is a shell function that you write, that aborts this deployment. In other words, we will:

  1. Use git fetch origin to update our local Git repository with new commits from origin, updating origin/master, origin/develop, and so on.

    This is what the first line, git fetch origin || die ... does. Again, you must write the die function.

  2. Save the current hash ID so that we know which commit was current before we ran git merge --ff-only.

  3. Run git merge --ff-only so that we get a fast-forward merge, or a failure if fast-forward merging is not possible (if a real merge is required).

    (Note: if you want to try a real merge for this case, this complicates deployment! You'll need to decide what to do if the real merge stops to get help from the user.)

If all of the above go well, you're now ready to compare what was in the HEAD commit before the git merge --ff-only to what is in the HEAD commit after the git merge --ff-only. You do this with a simple git diff, or for reliability in a script, git diff-tree. (The git diff-tree command is a little bit harder to use in general, but is not affected by per-user configurations, so it's wise to use git diff-tree in any script that is not user-oriented.) So now you can run:

git diff --name-only $starthash HEAD

or:

git diff-tree --name-only $starthash HEAD

In this case, both produce the same output except that git diff-tree has the rename detector turned off, regardless of the user's git diff configuration. This will only make a visible difference if the rename detector is on and finds a rename, and even then it will be non-obvious unless you were to switch from --name-only to --name-status: you'll see that one add-file and one delete-file are replaced with one rename-file-from-old-name-to-new-name operation.

Community
  • 1
  • 1
torek
  • 448,244
  • 59
  • 642
  • 775
  • Thank you for your insight on "don't use git pull in your deployment script". I'll refactor. And this: `starthash=$(git rev-parse HEAD)` — is the gem I was looking for! Thank you again :-) – pilat Feb 17 '20 at 08:19
0

You can try git diff --name-only ??LAST_GIT_PULL_RANGE?? --oneline.
This works fine for me.

  • does not work for me as `gid diff` shows the current difference between the last commit and the staging for the next commit. – Timo Apr 13 '23 at 09:51